KMP算法
2017-01-02 19:01
155 查看
2017.01.02
洛谷3375:#include<cstdio> #include<cstring> int l1,l2,i,j; char a[1000001]; char b[1001]; int next[1001]; int main() { scanf("%s%s",a+1,b+1); l1=strlen(a+1),l2=strlen(b+1); j=0; for(i=2;i<=l2;i++) { while(j&&b[i]!=b[j+1]) j=next[j]; if(b[i]==b[j+1]) j++; next[i]=j; } j=0; for(i=1;i<=l1;i++) { while(j&&a[i]!=b[j+1]) j=next[j]; if(a[i]==b[j+1]) j++; if(j==l2) { printf("%d\n",i-l2+1); j=next[j]; } } for(i=1;i<=l2;i++) printf("%d ",next[i]); return 0; }
为什么呢?
首先第一个for循环,在需要匹配的字符串b内进行自身匹配。
①while循环 寻找第一次出现的能和字符串b内前j-1位匹配的和b[j]相同的字符的位置。
②如果不是由于j=0导致while中断则匹配位数+1,并赋给next数组。
第二个for循环长得和第一个很像,就是将字符串a与字符串b匹配。
第三个for:输出next数组。(只因luogu要输出)
end.
至于为什么要叫next数组?我也不知道。其实next中存储的是能和之前前缀字符串匹配的第一个字符出现的位置,个人认为叫做prev可能更容易理解。
2017.01.22(续):
今天上午gty学长给我们讲了KMP。KMP大致有两个过程:
①计算失配函数
②字符串比较
以poj3461为例:传送门
题目简介:给定多对字符串,求第一个字符串在第二个字符串里出现的次数。
①
计算失配函数时,字符串第0位的函数值设为-1,便于以后程序的执行。
接下来从0~strlen(a)-1,依次计算下一位的函数值(包括字符串最后’\0’)。
求解f[i+1]时,由于f[i]已经求出,即图中斜线部分代表的子串相同。
若a[j]=a[i],则j即为i+1的函数值;
若a[j]≠a[i],则j=f[j],由于f[f[i]]已经求出,即图中红色等号部分代表的字串相同(图上方的f[j]等为f[j]代表的相等字符串区间),若此时a[j]=a[i],则此时的j为i+1的函数值,以此类推,直至求出或j=-1(到达字符串的第0位)。
②
字符串比较(中心部分)
与①类似但稍有不同,不细讲了。
详见代码:
#include<cstdio> #include<cstring> char a[1000010]; char b[1000010]; int f[1000010]; int n,i; inline void makefail() { int j,len=strlen(a); f[0]=-1; for(int i=0;i<len;i++) { j=f[i]; while(j!=-1&&a[j]!=a[i]) j=f[j]; f[i+1]=++j; } } inline int kmp() { int j=0,lena=strlen(a),lenb=strlen(b),ans=0; for(int i=0;i<lenb;i++) { while(j!=-1&&a[j]!=b[i]) j=f[j]; j++; if(j==lena) { ans++; j=f[j]; } } return ans; } int main() { scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%s%s",a,b); makefail(); printf("%d\n",kmp()); } return 0; }