这里介绍一下KMP算法,它是求两个串匹配问题,术语叫串的模式匹配
即给出两个字符串a,b,求出一个数k(假设一定存在),使得所有的b[i] = a[k+i](一般是a串比b串长)
起初是求字符串的,但是字符串和数组基本一样,所以求数字序列也可以,拿字符串来讲解
序列A:babababc
序列B:ababc
以这个为例,next[0]表示B[0]与A[0]不匹配时,以A[1]为新起点应与B的哪一位字符开始比较,显然要从B[0],即从头开始,next[0]=0
而便于后边的计算,将next[0]初始化为-1
如:babababc ---> babababc
ababc ababc
next[4]时,表示B[0]到B[3]均与A某一段匹配,这时A[5]与B[2]开始匹配就可以了,next[4] = 2
如:babababc ---> babababc
ababc ababc明显的next[i]表示类似(不知道怎么表达,就类似吧)上述B串的前i项重复的长度
无, 即A的某个字符与B[0]都不同,下次肯定还要从头开始
a,重复是0,即下次匹配依旧从头开始
ab,重复是0,即下次匹配依旧从头开始
aba,重复是1,即下次匹配从b即B[1]开始
abab,重复是2,即下次匹配从a即B[2]开始
ababc,完全匹配
就是i表示B[0]到B[i]与A串某一段完全匹配,但B[i+1]不匹配,0<=j<=i,i<B串的长度
0 1 2 ......... j
| | | | |
i-j i-j+1 i-j+2 ......... i 这样对应的关系,完全相等的最长长度,即上述不可描述的重复长度
void Getnext(){ int i, j; i = 0; next[0] = j = -1; while(i < S-1){ if(j == -1 || s[i] == s[j]){ i++; j++; next[i] = j; } else{ j = next[j]; } } } 核心代码就是这个了,next[j]就是前j项满足上述重复的长度,很容易理解,不然找几组数据代入试一下,就明白了核心只与短的那个串有关,与被查找的长串即主串无关
有时候看代码比看长篇大论的描述理解的要快
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long #define maxn 10010 #define Maxn 1000010 #define inf 0x3f3f3f3f #define mes(a, b) memset(a, b, sizeof(a)) int l[Maxn], s[maxn], next[maxn]; int L, S; void Getnext(){ int i, j; i = 0; next[0] = j = -1; while(i < S-1){ if(j == -1 || s[i] == s[j]){ i++; j++; next[i] = j; } else{ j = next[j]; } } } int kmp(){ int i, j; i = j = 0; while(i < L){//依据next的优化进行遍历 if(j == -1 || s[j] == l[i]){ i++; j++; } else{ j = next[j]; } if(j == S){ return i-S+1; } } return -1; } int main(){ int t; scanf("%d", &t); while(t--){ scanf("%d%d", &L, &S); for(int i = 0; i < L; i++){ scanf("%d", &l[i]); } for(int i = 0; i < S; i++){ scanf("%d", &s[i]); } if(L < S){ printf("-1\n"); continue; } Getnext(); printf("%d\n", kmp()); } return 0; }
给出串的模板,其实一样的,只是把int改成char而已
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long #define maxn 10010 #define inf 0x3f3f3f3f #define mes(a, b) memset(a, b, sizeof(a)) char l[maxn], s[maxn]; int next[maxn]; int L, S; void Getnext(){ int i, j; i = 0; next[0] = j = -1; while(i < S-1){ if(j == -1 || s[i] == s[j]){ i++; j++; next[i] = j; } else{ j = next[j]; } } } int kmp(){ int i, j; i = j = 0; while(i < L){ if(j == -1 || s[j] == l[i]){ i++; j++; } else{ j = next[j]; } if(j == S){ return i-S; } } return -1; } int main(){ while(~scanf("%s%s", l, s)){ L = strlen(l); S = strlen(s); Getnext(); printf("%d\n", kmp()); } return 0; }