重复的子字符串:Java解法详解
来源:459. 重复的子字符串 - 力扣(LeetCode)
题目
给定一个非空的字符串 s
,检查是否可以通过由它的一个子串重复多次构成。
示例 1:
输入: s = "abab" 输出: true 解释: 可由子串 "ab" 重复两次构成。
示例 2:
输入: s = "aba" 输出: false
示例 3:
输入: s = "abcabcabcabc" 输出: true 解释: 可由子串 "abc" 重复四次构成。 (或子串 "abcabc" 重复两次构成。)
提示:
1 <= s.length <= 104
s
由小写英文字母组成
详解
方法一:枚举法(滑动窗口思想)
思路
题目意思是判断一个字符串能否由其子串多次重复构成,可以提取三个重要点:
-
主串 一定是 子串长度 的倍数;
-
子串 长度一定小于或等于 主串 长度的一半;
-
子串 一定是 主串 的前缀;
代码
class Solution {
public boolean repeatedSubstringPattern(String s) {
int n = s.length();
for (int i = 1; i * 2 <= n; ++i) {
if (n % i == 0) {
boolean match = true;
for (int j = i; j < n; ++j) {
if (s.charAt(j) != s.charAt(j - i)) {
match = false;
break;
}
}
if (match) {
return true;
}
}
}
return false;
}
}
详细执行过程
-
获取字符串长度:首先,代码获取输入字符串
s
的长度n
。 -
遍历可能的子串长度:使用一个循环从
1
到n/2
遍历可能的子串长度i
。这里只需要遍历到n/2
,因为如果一个子串的长度超过n/2
,它不可能重复多次来构成s
。 -
检查是否为子串的倍数:在每次循环中,首先检查
n
是否能被i
整除。如果n
能被i
整除,说明s
可以被分成若干个长度为i
的子串。 -
匹配子串:如果
n
能被i
整除,进入内层循环,从位置i
开始遍历字符串s
,检查每个字符是否与其对应的前一个子串中的字符相同。具体来说,检查s.charAt(j)
是否等于s.charAt(j - i)
。 -
判断匹配结果:
- 如果在内层循环中发现任何不匹配的字符,将
match
标志设为false
并跳出循环。 - 如果所有字符都匹配,说明整个字符串可以由长度为
i
的子串重复构成,返回true
。
- 如果在内层循环中发现任何不匹配的字符,将
-
返回结果:如果遍历完所有可能的子串长度后仍未找到匹配的子串,则返回
false
,表示字符串s
不能由某个子串重复多次构成。
方法关键(滑动窗口)
s.charAt(j) != s.charAt(j - i)
比较字符串 s
中两个位置的字符是否相等。具体来说,它是在检查字符串 s
中索引为 j
的字符和索引为 j - i
的字符是否相同。
举例
对于输入字符串 asdasdasd
,我们来逐步分析该算法的执行过程:
-
字符串长度:首先计算字符串
asdasdasd
的长度,得到n = 9
。 -
遍历可能的子串长度:根据算法,我们从
1
到n/2
(即4
)遍历可能的子串长度i
。-
i = 1:
- 检查
n
是否能被1
整除,显然可以。 - 进行匹配检查:逐个比较每个字符与其前一个位置的字符。
s.charAt(1) != s.charAt(0)
,即's'
和'a'
不相等。
- 匹配失败,
match
为false
,跳出内层循环。
- 检查
-
i = 2:
- 检查
n
是否能被2
整除,显然不能(9 不能被 2 整除),因此跳过这个长度。
- 检查
-
i = 3:
- 检查
n
是否能被3
整除,可以(9 能被 3 整除)。 - 进行匹配检查:
s.charAt(3) == s.charAt(0)
,即'a'
和'a'
相等。s.charAt(4) == s.charAt(1)
,即's'
和's'
相等。s.charAt(5) == s.charAt(2)
,即'd'
和'd'
相等。s.charAt(6) == s.charAt(3)
,即'a'
和'a'
相等。s.charAt(7) == s.charAt(4)
,即's'
和's'
相等。s.charAt(8) == s.charAt(5)
,即'd'
和'd'
相等。
- 所有字符都匹配,说明整个字符串可以由长度为
3
的子串'asd'
重复构成,返回true
。
- 检查
-
若仍有疑问,可直接运行下方完整代码进行调试。建议设置断点逐步跟踪执行流程,这样能更直观地理解程序的运行过程。
class Solution {
public boolean repeatedSubstringPattern(String s) {
int n = s.length();
for (int i = 1; i * 2 <= n; ++i) {
if (n % i == 0) {
boolean match = true;
for (int j = i; j < n; ++j) {
if (s.charAt(j) != s.charAt(j - i)) {
match = false;
break;
}
}
if (match) {
return true;
}
}
}
return false;
}
}
public class Main {
public static void main(String[] args){
Solution solution = new Solution();
String test1 = "aba";
// 调用方法并输出结果
System.out.println("Test 1: " +
solution.repeatedSubstringPattern(test1)); // 应输出 true
}
}
方法二(拼接检测方法)
要判断主串是否由子串重复构成,可以采用拼接检测方法:取出潜在子串并添加到主串末尾,同时移除主串前端等长子串。若处理后字符串与原主串相同,则证明主串确实由该子串重复构成。
class Solution {
public boolean repeatedSubstringPattern(String s) {
for (int i = 1;i*2 <= s.length(); i++) {
//将s.chatAt(i)的元素拼接到s后面
String str = s.substring(0, i);
//将s.chatAt(i)的元素拼接到s后面
String str2 = s + str;
//拿到str2里面从i之后的元素
String str3 = str2.substring(i);
if (str3.equals(s)){
return true;
}
}
return false;
}
}
知识点
s.substring()
substring
方法是 Java 中 String
类的一个方法,用于从字符串中提取子串。它有两个重载版本:
-
public String substring(int beginIndex)
:- 该方法返回从指定索引
beginIndex
开始到字符串末尾的子串。 - 例如,
"hello".substring(2)
将返回"llo"
。
- 该方法返回从指定索引
-
public String substring(int beginIndex, int endIndex)
:- 该方法返回从指定索引
beginIndex
开始,到endIndex
结束(不包括endIndex
)的子串。 - 例如,
"hello".substring(1, 4)
将返回"ell"
。
- 该方法返回从指定索引
参数说明
beginIndex
:子串的起始索引(包含)。endIndex
:子串的结束索引(不包含)。
注意事项
- 索引从 0 开始。
- 如果
beginIndex
和endIndex
相同,返回的子串为空字符串。 - 如果
beginIndex
大于endIndex
或任一索引超出字符串长度,会抛出StringIndexOutOfBoundsException
异常。