KMP算法思路:
1.求解(最长相等前后缀)next数组(如字符串aabaaac,next数组为[0,1,0,1,2,2,0]):
什么是前缀?
包含第一个字符,不包含最后一个字符的字符串,字符串aabaaac的前缀为a、aa、aab、aaba、aabaaa;
什么是后缀?
不包含第一个字符,包含最后一个字符的字符串,字符串aabaaac的后缀是c、ac、aac、aaac、baaac、abaaac;
最长前后缀相等的条件是什么?
使用i表示后缀末尾,使用j表示前缀末尾,下标j和下标i对应的字符相等,就得到了最长相等前后缀,就是从0-j的字符串,长度为j+1。
如果前后缀不相等,应该如何操作?
当i表示的后缀末尾和j表示的前缀末尾字符不相等时,需要移动前缀结尾j,直到i和j所指字符相等为止。
如何移动前缀末尾j呢?
首先明确next数组的元素定义为最长相等前后缀的长度。当以下标i结尾的后缀和以下标j结尾的前缀不匹配时,也就说明以i结尾的后缀和以j结尾的前缀字符串除了最后一个字符不相同,剩下的字符都相同。比如字符串aabaaac,当i=5,j=2时,前缀为aab,后缀为aaa,next数组为0,1,0,1,2,移动j=2到j=next[j-1]=1的位置,也就是去掉最后一个不匹配字符后的前缀字符串[0,j-1]的最长相等前后缀的字符串为[0,next[j-1]],也就是aab去掉b后aa的位置再进行比较,重复这个过程直到j=0或者字符串相等。
代码如下:
public int strStr(String haystack, String needle) {
int[] next = this.next(needle);
int i = 0,j = 0;
while(i < haystack.length() && j < needle.length()) {
if(needle.charAt(j) == haystack.charAt(i)) {
i++;
j++;
} else if(j <= 0){
i++;
} else {
j = next[j-1];
}
}
return j == needle.length() ? i - j : -1;
}
public int[] next(String str) {
if(str.length() == 0) {
return new int[0];
}
int[] next = new int[str.length()];
next[0] = 0;
int j = 0;
//j是前缀末尾,j是后缀末尾,j是next数组的值
for(int i = 1; i < str.length();i++) {
while(str.charAt(i) != str.charAt(j) && j > 0) {
j = next[j-1];
}
if(str.charAt(i) == str.charAt(j)) {
next[i] = ++j;
} else {
next[i] = 0;
}
}
return next;
}
459.重复的子字符串
leetcode题目链接:https://leetcode.cn/problems/repeated-substring-pattern
leetcode AC记录:
思路:
KMP算法,求出next数组,字符串长度减去数组的最后一位数字为重复长度。
代码如下:
public boolean repeatedSubstringPattern(String s) {
int[] next = this.next(s);
if(0 == next[s.length()-1]) {
return false;
}
return s.length() % (s.length() - next[s.length() -1]) == 0;
}
public int[] next(String s) {
int[] next = new int[s.length()];
next[0] = 0;
int j = 0;
for(int i = 1;i < s.length();i++) {
while(s.charAt(i) != s.charAt(j) && j > 0) {
j = next[j-1];
}
if(s.charAt(i) == s.charAt(j)) {
next[i] = ++j;
}
}
System.out.println(Arrays.toString(next));
return next;
}