基本思想
编辑
设主串(下文中我们称作T)为:a b a c a a b a c a b a c a b a a b b
模式串(下文中我们称作W)为:a b a c a b
用暴力算法匹配字符串过程中,我们会把T[0] 跟 W[0] 匹配,如果相同则匹配下一个字符,直到出现不相同的情况,此时我们会丢弃前面的匹配信息,然后把T[1] 跟 W[0]匹配,循环进行,直到主串结束,或者出现匹配成功的情况。这种丢弃前面的匹配信息的方法,极大地降低了匹配效率。
而在KMP算法中,对于每一个模式串我们会事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数。
在第一次匹配过程中
T: a b a c a
a b a c a b a c a b a a b b
W: a b a c a
b
在T[5]与W[5]出现了不匹配,而T[0]~T[4]是匹配的,现在T[0]~T[4]就是上文中说的
已经匹配的模式串子串,现在移动找出
最长的相同的前缀和后缀并使他们重叠:
T: a b a c a
a b a c a b a c a b a a b b
W: a
b a c a
b
然后在从上次匹配失败的地方进行匹配,这样就减少了匹配次数,增加了效率。
然而,如果每次都要计算
最长的相同的前缀反而会浪费时间,所以对于模式串来说,我们会提前计算出每个匹配失败的位置应该移动的距离,花费的时间就成了常数时间。
NEXT数组:
两张图:
模板:
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <cstring> #include <cmath> #include <vector>#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <cstring> #include <cmath> #include <vector> #include <map> #include <queue> #include <deque> #include <stack> using namespace std; typedef long long ll; const int maxn = 1000000+5; int next[maxn]; char s[maxn],a[maxn]; void cal_next(char *a){ memset(next,-1,sizeof(next)); int len = strlen(a); int k = -1 ; next[0]=-1; for(int p = 1 ; p < len ; p++){ while(k>-1&&a[p]!=a[k+1]) k = next[k]; if(a[k+1]==a[p]) next[p]=++k; } } int kmp(char *s,char *a){ int slen = strlen(s); int alen = strlen(a); int k = -1; for(int i = 0 ; i < slen ; i++){ while(k>-1&&a[k+1]!=s[i]) k = next[k]; if(a[k+1]==s[i]) k++; if(k == alen - 1){ return i; } } return 0; } int main(){ int kase; scanf("%d",&kase); while(kase--){ scanf("%s",a); scanf("%s",s); cal_next(a); printf("%d\n",kmp(s,a)); } }
其他的回来补坑。。。