标签:
在简单的模式匹配时,每次遇到s[i]!=t[j] 时,每次都要讲i回溯。这往往浪费了很多时间。KMP算法就是当遇到s[i]!=t[j] 时,只让t[j] 回溯而s[i]不用回溯,这样就节省了很多时间。
当t[1]t[2]…t[j-1]=s[i-j+1]s[2]…s[i-1]成立时,若t[j]!=s[i],则此时根据KMP算法只用回溯t[j].
假设有k( k< j)使t[1]t[2]…t[k-1]=s[i-k+1]s[i-k+2]…s[i-1]
可见若有k则不用让t从1开始匹配了,从k开始匹配就行了。可见主要就是求k的值。
因为 t[1]t[2]…t[j-1]=s[i-j+1]s[i-j+2]…s[i-1]
因为k将t[1]t[2]…t[j-1]分成了两部分。因为成立,可知 t[j-k+1]t[j-k+2]…t[j-1]=s[i-k+1]s[i-k+2]…s[i-1] ;
由得。t[1]t[2]…t[k-1]=t[j-k+1]t[j-k+2]…t[j-1]。所以k的值满足这个等式。由这个等式就可以得到k的值。
记字符串t中t[j]的k值为next[j];
下面就是求next[j]:
当j=1时 next[1]=0.
当有k满足t[1]t[2]…t[k-1]=t[j-k+1]t[j-k+2]…t[j-1]时。next[j]=k,当再往右移动一个元素时,当t[k]=t[j]时t[1]t[2]…t[k-1]t[k]=t[j-k+1]t[j-k+2]…t[j-1]t[j].此时next[j+1]=k+1=next[j]+1.
当t[k]!=t[j]时,此时,就应当求出一个k1值使t[1]t[2]…t[k1-1]t[k1]=t[j-k1+1]t[j-k1+2]…t[j-1]t[j]成立。此时已经有t[1]t[2]…t[k-1]=t[j-k+1]t[j-k+2]…t[j-1]成立,所以要想将前一个等式成立的话,就相当于模式匹配了(字符串t即相当于主串又相当于模式)。所k1=next[k],且
t[k1]=t[j].则说明在主串中第j+1个字符之前存在一个最大长度的k1的子串t[1]t[2]…t[k1-1]t[k1]=t[j-k1+1]t[j-k1+2]…t[j-1]t[j].
因此这时候next[j+1]=k1+1=next[k]+1。
若t[k1]!=t[j]则继续往后推。k2=next[k1]=next[next[k]].若k2满足t[1]t[2]…t[k2-1]t[k2]=t[j-k2+1]t[j-k2+2]…t[j-1]t[j]. 则next[j+1]=k2+1=next[next[k]]。
如果不符合就继续往下推导。直到tj和模式中的某个字符匹配成功或者不存在任何ki满足
t[1]t[2]…t[ki-1]t[ki]=t[j-ki+1]t[j-ki+2]…t[j-1]t[j]. 则有next[j+1]=1.
下面就是求next的函数
void Count_next(char *t,int next[])
{
int i,j;
i=1;j=0;
next[i]=0;
while(i<t[0]){
if(j==0){j++;i++;next[i]=1;}
else if(t[j]==t[i]){j++;i++;next[i]=j;}
else if(t[j]!=t[i])j=next[j];
}
}
程序解析:程序中i的值就是遍历整个字符串,j的值就是记录每个next[i]。如果t[i]!=t[j]则++j,就是next[i]=next[i-1]+1;若不满足t[i]!=t[j]则j=next[j],就是使j回溯。
下面就是利用KMP算法实现模式匹配了。
int pattern(char *s,char *t,int next[])
{
int i=1,j=1;
while(i<=s[0]&&j<=t[0]){//如果相等就往后推移
if(j==0||s[i]==t[j]){i++;j++;}
else j=next[j];
}
if(j>t[0])
return i-t[0];//返回模式在主串中的起始位置
else
return -1;
}
标签:
原文地址:http://blog.csdn.net/u014104588/article/details/45102955