题目链接:
题目描述:
给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置。然后输出next数组
题解:
这篇题解鸽了好久,因为我一直都没有完全理解看猫片,这东西是真的难。KMP
的用处就是在母串里面找子串。
朴素做法是一位一位地匹配,判断子串和母串的字符是否相同,所以时间复杂度是O(mn)。(m、n是母串和字串的长度)
KMP
就是通过一些神奇的操作跳过一些字符,以达到优化时间复杂度的效果。
具体的做法理解了其实并不复杂,一共就是3步。
1.找出子串内最长相同前后缀
举个例子:子串是AABAABAA
那么A和AA都是相同前后缀,但由于AA比较长,所以我们就选择它
很简单吧。。。
2.在后缀的最后设置一个指针,指向其最长相同前缀的最后
后缀的最后其实就是字符串的最后啦。。。
这个指针就是所谓的next
数组
我们依次对子串的每一位进行这两步操作,第i位的指针就是next[i]
3.看代码。。。
int KMP(int len1,int len2){
//母串的长度为len1,子串的长度为len2
//s1时母串,s2是子串
k=0; //字串指针
for(int i=0;i<len1;i++){
//和朴素算法一样,依次匹配母串的每一位
while(k>0&&s1[i]!=s2[k]) k=next[k];
if(s1[i]==s2[k]) k++; //如果匹配成功,匹配下一位
if(k==len2) cout<<i-len2+2<<endl; //如果全部匹配成功,输出答案
}
}
重点解释一下没加注释的那行代码,在朴素算法中如果匹配失败我们要把字串的指针归零(下面有朴素算法可以自行比较一下),但在KMP算法中我们直接把指针返回到next[i]记录的点,也就是我上面说的"相同前缀的最后“,因为既然这一段后缀和前缀是相同的,那么也就没有匹配再次匹配这一段的必要了,可以直接从相同部分的后一位开始匹配,这样就能达到节省时间的目的。
再举个例子:
A | B | C | B | C | B | C | A | A |
---|---|---|---|---|---|---|---|---|
i |
||||||||
</ |