一开始感觉KMP算法非常难,但是其实多搜一些资料仔细研究一下发现其实。。
其实还是学习方法的问题吧,学的太潦草,不深入,仔细钻研,发现其实并不是那么难。
KMP算法是字符串匹配算法。
给定两个字符串a,b。统计串b在a中出现的位置与次数。
a= bacbababadababacambabacaddababacasdsd
b= ababaca
1.暴力枚举
bacbababadababacambabacaddababacasdsd
ababaca
每次让b串向后移一位。
bacbababadababacambabacaddababacasdsd
ababaca
2.借助next数组来实现。
KMP算法的核心也就在这个next数组上面。它主要存储了当匹配失败时,串b需要后移的位数。我们不单单是后移一位,我们预先处理出需要后移的位数,那么因为是预处理,所以next数组的计算也就和串a没有半毛钱关系。
next[j] = k.最关键的一条语句了。
假设现在有两个串,每个串从0开始存储:
S = ABCABCDHIJKOOL
T = ABCABB
在第5位发现匹配失败:
这时我们的移动方案:
S = ABCABCDHIJKOOL
T = ABCABB
移动完后我们直接从T串的第C开始继续匹配就OK。
显然,我们不可能真正的去移动,我们需要用两个指针(l,r)去模拟。
第一次在第五位发现匹配失败时,l = 5, r= 5.
然后移动后l = 5, r = 2.
显然当匹配失败时,r要移动到下一个位置,而l是不动的。r要移动到第k位。
我们观察上面的移动可以发现:
T串的前k个字符和r之前的最后k个字符相同,这里的r是指匹配失败时的r。
这里我们移动到了第2位,显然k=2.
T串的前2位:ABCABB
r之前的最后K位:ABCABB
显然r的取值范围是0~length(串的长度)-1。
我们用一个next数组来保留每一个r需要后移的位数K。
4.计算next数组。
next数组的计算只需要用到T串。
我们可以利用一种dp的思想来处理。
依然用这个串T = ABCABBC
我们先考虑j=0时,这个时候r无法移动,只能右移l。所以初始化为-1,即next[0] = -1;
再考虑j = 1,r只能移动到第0位。next[1] = 0;
j = 2,T[0] != T[1], next[2] = 0;
j = 3, next[3] = 0;
j = 4, next[4] = 1;
j = 5, next[5] = 2;
我们在计算next[5] 时显然可以利用next[4],因为T[j - 1] == T[next[j - 1]], 所以next[j] = next[j - 1] + 1.
那么
next[0] = -1;
int j = 0, k = -1;
while(j < m - 1){
if(k == -1 || T[j] == T[k]){
next[++j] = ++k;
} else k = next[k];
}
显然当T[j - 1] != T[k]时, next[j] = 0;
k = next[k] :
看这个串:ABACDABABC, C是第九位,此时k = 3, j = 8.(ABA)
此时C != B, k = next[3], 这里next[3]>0,j的前面有部分和整串的前缀相同, 所以我们可以直接把T[next[k]]和T[j]比较。
再看ABAB这个串,显然next[3] = 1;但是我们真的有必要从1再比较吗?没必要,因为T[1] = T[3]。
所以,上述代码可以再优化一下:
next[0] = -1;
int j = 0, k = -1;
while(j < m - 1){
if(k == -1 || T[j] == T[k]){
if(T[++j] == T[++k]) next[j] = next[k];
else next[j] = k;
} else k = next[k];
}
5.KMP比较
最后一步了,拿两个指针去模拟就行了。
#include<cstdio>
#include<cstring>
int n, m, l, r, k, ans;
char S[120], T[120];
int next[120];
int main(){
scanf("%s%s", S, T);
n = strlen(S);
m = strlen(T);
next[0] = -1;
int j = 0, k = -1;
while(j < m - 1){
if(k == -1 || T[j] == T[k]){
if(T[++j] == T[++k]) next[j] = next[k];
else next[j] = k;
} else k = next[k];
}
l = 0; r = 0;
while(l < n && r < m){
if(r == -1 || S[l] == T[r]){
l++;
r++;
}else r = next[r];
if(r == m){
printf("%d ", l - r);
ans++;
r = 0;
}
}
printf("\n%d", ans);
return 0;
}