代码随想录刷题day09
补KMP,当时没咋看懂,多看了几遍,有了一点自己的理解。
在主串中寻找子串,next数组(最长前缀数组)
前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。
后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。
aabaaf 来举例:
前缀有 a,aa,aab,aaba,aabaa,
后缀有f,af,aaf,baaf,abaaf
最长相等前后缀,前缀和后缀中相等的长度
a: 0 a没有前缀也没有后缀
aa: 1 a和a
aab: 0
aaba :1
aabaa: 2
aabaaf : 0
上面得到的这个序列 [0,1,0,1,2,0]就是前缀表
为什么要用前缀表?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SpdONgco-1667743140552)(代码随想录刷题.assets/image-20221106203552072.png)]
下标5之前这部分的字符串(也就是字符串aabaa)的最长相等的前缀 和 后缀字符串是 子字符串aa ,因为找到了最长相等的前缀和后缀,匹配失败的位置是后缀子串的后面,那么我们找到与其相同的前缀的后面从新匹配就可以了。
看了好几遍这段话,终于有点看明白了。最长相等前后缀记录了相等的位置,后缀的部分匹配不上了,跳到相等的前缀的后面,也就是b这个位置,因为aa是之前已经匹配过了不同在匹配。
代码上如何求这个前缀表呢?
1.初始化
2.处理前后缀不相同的情况:找到前一个需要回退的位置进行回退
3.处理前后缀相同的情况
4.更新next数组
private void getNext(int[] next, String s) {
int j = 0;//j指向前缀末尾位置,还代表最长前后缀长度,i指向后缀末尾位置
next[0] = 0;
for (int i = 1; i < s.length(); i++) {//i从1开始,以为要比较前后缀末尾是否相同的
while (j > 0 && s.charAt(j) != s.charAt(i)) //前后缀不相同的情况,
j = next[j - 1];
if (s.charAt(j) == s.charAt(i)) //前后缀相同的情况,因为j代表了最长前后缀的长度,所以需要++
j++;
next[i] = j;
}
}
得到next数组以后 如何利用next数组进行匹配呢
i指向文本串,j指向next数组,当遇到不匹配的时候 去找next数组中前一个记录的值;知道j指向了next数组的后面一位,说明找到了匹配的子串。
完整代码
class Solution {
public int strStr(String haystack, String needle) {
if(needle.length()==0) return 0;
int [] next=new int[needle.length()];
getNext(next,needle);
int j=0;//j 指向next数组的第一位
for(int i=0;i<haystack.length();i++){
while(j>0 && needle.charAt(j)!=haystack.charAt(i)){
j=next[j-1];
}
if(needle.charAt(j)==haystack.charAt(i)) j++;
if(j==needle.length()) return i-needle.length()+1;
}
return -1;
}
private int[] getNext(int[] next, String s){
int j=0;//指向前缀末尾的位置,还代表最长前后缀的长度
next[0]=0;
//i 指向后缀末尾的位置,因为要比较 所以从1 开始
for(int i=1;i<s.length();i++){
while(j>0 && s.charAt(j)!=s.charAt(i)){
//前后缀不相等 j跳到 next数组前一个记录 所记录的位置
j=next[j-1];
}
if(s.charAt(i)==s.charAt(j)) j++;//前后缀相等的情况,j还代表的最长相等前后缀的长度 所以还需要++
next[i]=j;//更新next 数组 当前i指向的数组 记录最长前后缀的长度 i++的操作已经在循环中做了
}
return next;
}
}
reference
代码随想录 (programmercarl.com)