KMP算法---处理字符串匹配
输入:字符串A,B
输出:B是否为A的子串
算法:
设A的长度为n,B的长度为m,即A=A[1…n],B=b[1…m]。
假设有两个索引:i,j,并且满足A[i-j+1…i]=B[1…j]相等,也就是说以i结尾的长度为j的A中子串匹配以j结尾的长度为j的B中子串。初始时i=j=0。现在要判断A[i+1]是否和B[j+1]是否相等,如果相等,则i和j都增加1,并且当j等于m时说明B是A的子串;如果不相等,则要重新规划j的值,变成j’,使得A[i-j'+1…i]=B[1…j'],那么怎么确定j’的值呢?要想满足A[i-j'+1…i]=B[1…j'],且已知条件A[i-j+1…i]=B[1…j],则要满足B[1…j']=B[j-j'+1…j]的最大j'值,这个值之和B字符串有关,所以可以进行预处理,对于每一个1=<j<=m,令j'=P[j]。
当P[j]确定时,A[i-P[j]+1…i]=B[1…P[j]],回到上一自然段的条件,继续推进即可。
当P[j]=0时怎么办,此时只有继续往后遍历i,知道找到A[i]=B[P[j]+1],继续推进。
举例:
现在有A=”abababaababacb”,B=“ababacb”
现在i=j=4,即A[1…4]=B[1…4],
i = 1 2 3 4 5 6 7 8 9 10 11 12 13 14
A= a b a b a b a a b a b a c b
B= a b a b a c b
j = 1 2 3 4 5 6 7
现在判断A[5]和B[5],发现相等,i和j同时递增,此时A[1…5]=B[1…5]
i = 1 2 3 4 5 6 7 8 9 10 11 12 13 14
A= a b a b a b a a b a b a c b
B= a b a b a c b
j =1 2 3 4 5 6 7
现在i=j=5,判断A[6]和B[6],发现不等,我们查找P[5],发现是3,则新的匹配是A[3…5]=B[1…3]
i = 1 2 3 4 5 6 7 8 9 10 11 12 13 14
A=a b a b a b a a b a b a c b
B= a b a b a c b
j= 1 2 3 4 5 6 7
现在i=5,j=3,判断A[6]和B[4],发现相等,i和j同时递增,此时A[3…6]=B[1…4]
i = 1 2 3 4 5 6 7 8 9 10 11 12 13 14
A=a b a b a b a a b a b a c b
B= a b a b a c b
j= 1 2 3 4 5 6 7
由于A[7]=B[5],继续推进得到上图,发现A[8]!=B[6],则要算出P[5],然后继续。。。
//初始化j为0 int j=0; //i只会一直向前进 for(int i=1;i<=n;++i) { //不能匹配的时候,去找P[j],直到能够匹配,或者直到j=0 while(j>0 && B[j+1]!=A[i]) j=P[j]; //能够匹配,继续推进 if(B[j+1==A[i]]) ++j; //推进到B串的最后,输出 if(j==m) { printf "substring at A's" i-m "position"; //此处是为了继续推进查找,因为子串索引可能不止一处 j=P[j]; } }
下面还剩一个问题,即P[j]怎么求。
P[1]=0; j=0; for(int i=2;i<=m;++i) { while(j>0 && B[j+1]!=B[i]) j=P[j]; if(B[j+1]==B[i]) j=j+1; P[i]=j; }