459. 重复的子字符串
提示:Java中转为字符数组,不使用charAt(),效率会更高。
思路1:暴力解法。起始下标从0开始,第一个for循环寻找子串的结束位置,如果子串大于字符串的一般就不会再有符合条件的了,结束循环。第二个for循环用来判断子串是否符合条件。
时间复杂度O(n^2)
class Solution {
public boolean repeatedSubstringPattern(String s) {
char[] ch = s.toCharArray();
boolean target = false;
for (int i = 0; i < ch.length / 2; i++) {
int k = 0;
for (int j = 0; j < ch.length; j++, k++) {
if (k > i) {
k = 0;
}
if (ch[j] != ch[k]) {
break;
}
}
if (k == i + 1) {
target = true;
}
}
return target;
}
}
思路2:移动匹配。如果字符串内部可以由字串组成,那么用s+s进行拼接,一定能够从内部再找到一个s。借用随想录中的图片理解一下,如果s如下图。
那么s+s就如下图所示。
注意:拼接后的字符串要删除首字符和尾字符,防止在s+s中找出原来的s。
时间复杂度O(m + n)
class Solution {
public boolean repeatedSubstringPattern(String s) {
// 移动匹配
StringBuilder sb = new StringBuilder();
sb.append(s.substring(1));
sb.append(s.substring(0, s.length() - 1));
if (sb.indexOf(s) != -1) {
return true;
}
return false;
}
}
思路3:kmp。重点在于,在由重复子串组成的字符串中,最长相等前后缀不包含的子串就是最小重复子串。具体推导过程移步随想录b站讲解。了解这句话后,我们只需要求解next数组,然后判断最小重复子串不是整个字符串,并且最小重复子串的长度能够被字符串的长度整除,则说明最小重复子串可以拼接成字符串。
时间复杂度O(n)
class Solution {
public boolean repeatedSubstringPattern(String s) {
// kmp
// 在由重复子串组成的字符串中,最长相等前后缀不包含的子串就是最小重复子串
// 求next数组
char[] ch = s.toCharArray();
int len = ch.length;
int[] next = new int[len];
int j = 0;
next[0] = 0;
for (int i = 1; i < len; i++) {
while (j > 0 && ch[i] != ch[j]) {
j = next[j - 1];
}
if (ch[i] == ch[j]) {
j++;
}
next[i] = j;
}
// 如果存在最长相等前后缀,并且最小重复子串的长度能够被字符串长度整除,说明符合条件
if (next[len - 1] != 0 && len % (len - next[len - 1]) == 0) return true;
return false;
}
}
字符串总结
双指针法
字符串的题目有的和数组的操作十分相似,可以考虑双指针法。双指针法在数组,链表和字符串中很常用。
反转的题目
这里的很多题目可以考虑,先整体反转再局部反转,或者先局部反转再整体反转。
kmp
kmp主要在匹配子串的题目中用到。重点在于求解next数组。
双指针法总结
总结一些双指针法做过的题目。双指针法通常用来降低时间复杂度,提高效率。随想录中双指针总结(十分清晰,必看!)