字符串算法~KMP
Posted whitelily
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了字符串算法~KMP相关的知识,希望对你有一定的参考价值。
字符串算法~KMP
有个视频讲的挺好的:
首先给一个字符串s,与另外一个字符串q,判断q是否是s的子串。
如何判断,先考虑暴力判断,枚举s字符串的每一位作为开头与q比较是否与q的每一位都相同,不相同及时break进入q的下一位继续从头开始比较,这样暴力判断其实也很快,一般情况下与KMP也没差多少,但是有部分样例,可以把暴力匹配的这种方式卡的很慢,比赛出KMP题的话,这种样例也是必有的。
举个栗子:s为aaaaazzzzz,q为aaaaaa;
s刚好少q一个a,那么基本上大多数的比较都是快吧q比较完了,结果最后不行,所以时间复杂度被卡成了O(N*M),N,M分别表示两个字符串的大小。KMP的在这里的作用就是用来快速过这种样例的,那么我们思考一下这种样例有什么特点,是不是q的a有点太多了,类似一种前后缀相似(像abcabcabc,azazaz也是如此),KMP就是利用了这种相似的特点。(先停一下,如果q没有这种相似那怎么办,放心q的相似越少,那么在与s比较的过程中大部分情况会越快被break掉,为什么的话可以自己想一想)。
现在我给一个新的样例来模拟一下:
s:1 2 1 2 3 1 2 3 1 3 2 1 2
q:1 2 3 1 3
第一次:1/1;
第二次:12/12;
第三次:12(1)/12(3);不同
第四次:(2)/(1);不同
第五次:1/1;
第六次:12/12;
第七次:123/123;
第八次:1231/1231;
第九次:1231(2)/1231(3);不同
这次不一样了,按理说我们要从s的第4位2继续找,但是我现在是不是知道s的第6位是1了,或者说s的第6位与q的第4位相等,因为在q的第5位,s的第7位不同,所以知道q的前4位与s对应相等,同时我如果我对q进行一些预处理的话,我也能知道q的第4(4至4位)位与其第1(1至1位)位相等及s的第6位与q的第1位相同,那么我们是不是可以直接拿s的第7位与q的第2位去比较了,跨过了s的4,5,6位以达到优化时间的目的,KMP的关键就是预处理next数组,总时间复杂度为O(n+m)。
next数组的目的再强调一边,找到对应位置往前最长的与前缀相同的长度,如abcdabc,最后一个c的可与前两个ab组成abc与前缀abc相同,next在第i位的意思是如果第i位发生错误(与比较字符串不同),前i-1位找到的前缀的末尾位置加一;
字符串: a b c d a b c @
数组为:-1 0 0 0 0 1 2 3
原理和表示说完了,之后就是模拟过程,不多说了,给个板子自己学一下:
第一个while是构造next数组,第二个while是匹配字符串;
#include<iostream>
#include<cstring>
using namespace std;
int t,n,m,a[1000007],b[100007],nextp[100007];
int kmp(int a[],int b[]){
int l=-1,r=0;
nextp[0]=-1;
while(r<m){
if(l==-1||b[l]==b[r]){
r++;
l++;
nextp[r]=l;
}
else{
l=nextp[l];
}
}
l=0,r=0;
while(r<n&&l<m){
if(a[r]==b[l]||l==-1){
r++;
l++;
if(l==m){
return r-m;
}
}
else{
l=nextp[l];
}
}
if(l==m){
return r-m;
}
else{
return -2;
}
}
int main(){
scanf("%d",&t);
while(t--){
memset(nextp, -1, sizeof(nextp));
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
for(int j=0;j<m;j++){
scanf("%d",&b[j]);
}
printf("%d
",kmp(a,b)+1);
}
}
以上是关于字符串算法~KMP的主要内容,如果未能解决你的问题,请参考以下文章