KMP
作用是找大串里匹配的小串的位置
理解好难,先讲一下原理。找目前字符串的Border:是一个字符串最长的,能跟真后缀相同的真前缀的长度。
比如ABCDABCD
从1号到八号 Border分别是00001234.。
如何求Border:
我们以ABABAC为例
用肉眼明显知道
001230
我们知道如果前面的字符串对称,只需要把当前字符和前面字符对称总数+1比较,如果继续对称,那么对称总数+1,否则
我们回到F[前一个总对称数]+1,这个字符比较,如果相等,下次比较则比较这个字符串的下一个即刻,如果依然不等,继续上述操作直到起点。
感觉没讲得很清楚,看了一位大佬的博客,理解了一点,结合学长的代码,模拟了一下。
https://blog.csdn.net/u011564456/article/details/20862555
大佬链接。
求出了Border.。我们需要利用Border进行优化匹配。
还是刚才那个道理,匹配字符看Boeder,即看是否有无效的匹配。
如
ABCBABCAAB
ABCAA
匹配到第4个就无法匹配了,我们此时可以利用Boeder,直接移动三位而不是一位。
从而产生优化的效果。
例如:
学长的例子
•P=ABCDABD
•T=ABCABCDABABCDABCDABDE
•从T的第一个位置开始匹配:
123456789012345678901
ABCABCDABABCDABCDABDE
ABCDABD
1234567
•在P的第4个字符匹配失败!
•我们到此成功匹配到了P的3个字符,而F[3]=0
- 所以也无法在??T_2, ?? T_3处继续产生匹配;
•把P串向右移动k-F[k]=3个字符
•
123456789012345678901
ABCABCDABABCDABCDABDE
ABCDABD
1234567
•我们到此成功匹配到了P的3个字符,而F[3]=0
- 所以也无法在??T_2, ?? T_3处继续产生匹配;
•把P串向右移动k-F[k]=3个字符
•
123456789012345678901
ABCABCDABABCDABCDABDE
ABCDABD
1234567
此时我们需要移动4而不是6.
我们可以知道F[6]=2;
即移动k-F[k]=4;
#include <bits/stdc++.h>
using namespace std;
const int maxn=1000050;
int F[maxn];
char P[maxn],T[maxn];
void getF(char *P,int m) {
F[1]=0;
for(int i = 2; i <= m; ++i) {
int k=F[i-1];
while(k&&P[k+1]!=P[i]) k=F[k];
if(P[k+1]==P[i]) ++k;
F[i]=k;
}
}
void getMatch(char *P,int m,char *T,int n)
{
getF(P,m);
int k=0;
for(int i = 1; i <= n; ++i) {
while(k&&T[i]!=P[k+1]) k=F[k];
if(T[i]==P[k+1]) ++k;
if(k==m) {
printf("matched at %d\n", i-m+1);
k=F[k];
}
}
}
int main() {
scanf("%s%s", T+1,P+1);
getMatch(P,strlen(P+1),T,strlen(T+1));
return 0;
}
学长的代码。镇楼。