提问:假设存在一个长度为n的字符串T,模板是一个长度为m的字符串P,且m<=n。求出所有模板在字符串中的匹配点。
匹配点即为:满足T[i]=P[0],T[i+1]=P[1],.........,T[i+m-1]=P[m-1]的点i;
最简单的办法就是挨个找,如果有不对的,就换下一个匹配点。时间复杂度最大为O(m*(n-1)),不是很理想。
于是,我们就想到了能否根据模板自身的规律减少时间复杂度?KMP算法就诞生了!
KMP算法最关键的就是一个失配函数F(i),什么就做失配函数呢?
F(i)代表如果你在对比模板第i位时,失配了也就是不一样了,我们要跳到与模板的第 F(i)位比较,而不是直接重新开始比较。
给出一个建立失配函数的模板:
样例: 零 一 二 三 四 五 六 七 八 九 十 十一
下面是我的模板:
给道题加深对F(i)函数的理解: uva 1328
根据我们求F(i) 函数的方法,可以看出,如果f(i)>0 代表 i前面的 f(i)个字符与前i-1个字符一样。
如果能形成循环必有 K*(i-F(i))=i;(K为整数) 仔细想想,必然成立;
我的代码:
#include <cstdio>
const int maxn = 1000000+10;
using namespace std;
char s[maxn];
int f[maxn];
int main (){
int n,kase=0;
while (scanf("%d",&n)!=EOF&&n){
scanf("%s",s);
f[0]=f[1]=0;
for (int i=1;i<n;i++){
int j=f[i];
while (j&&s[i]!=s[j]) j=f[j];
f[i+1]=s[i]==s[j]?j+1:0;
}
printf("Test case #%d\n",++kase);
for (int i=2;i<=n;i++){
if (f[i]>0&&i%(i-f[i])==0) printf("%d %d\n",i,i/(i-f[i]));
}
printf("\n");
}
return 0;
匹配点即为:满足T[i]=P[0],T[i+1]=P[1],.........,T[i+m-1]=P[m-1]的点i;
最简单的办法就是挨个找,如果有不对的,就换下一个匹配点。时间复杂度最大为O(m*(n-1)),不是很理想。
于是,我们就想到了能否根据模板自身的规律减少时间复杂度?KMP算法就诞生了!
KMP算法最关键的就是一个失配函数F(i),什么就做失配函数呢?
F(i)代表如果你在对比模板第i位时,失配了也就是不一样了,我们要跳到与模板的第 F(i)位比较,而不是直接重新开始比较。
给出一个建立失配函数的模板:
void getFail (char *P,int *f){ //构建失配函数 f为失配函数 P为模板串
int m=strlen(P);
f[0]=f[1]=0;
for (int i=1;i<m;i++){
int j=f[i];
while (j&&P[i]!=P[j]) j=f[j];
f[i+1]=P[i]==P[j]?j+1:0;
}
}
样例: 零 一 二 三 四 五 六 七 八 九 十 十一
p[i] A B R A C A D A B R A
f[i] 0 0 0 0 1 0 1 0 1 2 3 4
在失配函数知道的基础上我们写搜索函数就很好写了:下面是我的模板:
int find(char *T,char *P,int *f) { //P为模板串 T为比较串 f为失配函数
int n=strlen(T);
int m=strlen(P);
getFail(P,f);
int j=0;
for (int i=0;i<n;i++){
while (j&&P[j]!=T[i]) j=f[j]; //按照失配函数调整比较的位置
if (P[j]==T[i]) j++;
if (j==m) ans++; //找到子串 输出所在位置
}
return 0;
}
给道题加深对F(i)函数的理解: uva 1328
根据我们求F(i) 函数的方法,可以看出,如果f(i)>0 代表 i前面的 f(i)个字符与前i-1个字符一样。
如果能形成循环必有 K*(i-F(i))=i;(K为整数) 仔细想想,必然成立;
我的代码:
#include <cstdio>
const int maxn = 1000000+10;
using namespace std;
char s[maxn];
int f[maxn];
int main (){
int n,kase=0;
while (scanf("%d",&n)!=EOF&&n){
scanf("%s",s);
f[0]=f[1]=0;
for (int i=1;i<n;i++){
int j=f[i];
while (j&&s[i]!=s[j]) j=f[j];
f[i+1]=s[i]==s[j]?j+1:0;
}
printf("Test case #%d\n",++kase);
for (int i=2;i<=n;i++){
if (f[i]>0&&i%(i-f[i])==0) printf("%d %d\n",i,i/(i-f[i]));
}
printf("\n");
}
return 0;
}
为什么我这篇文章发的就这么费劲呢?T_T 深夜发,不管如何就是保存不成功。白天发也保存不成功,成功一次后发现,重复发了n回。又要重新删除。