背景:设两个串s和t,串t的定位就是在串s中找到一个与t相同的子串。S为目标串,t为模式串,因此串定位查找也叫模式匹配。KMP算法是一个效率较高的针对模式匹配的算法。
详解:
1.
从模式串中提取加速匹配的信息,即next表
在KMP算法中,通过分析模式串t从中提取加速匹配的有效信息。这种信息是对于串t的每个字符t[j](0<=j<=m-1)存在一个整数k(k < j),使模式串t中开头的k个字符(t[0],t[1],… ,t[k-1])依次与t[j]的前面k个字符(t[j-k],t[j-k+1],… ,t[j-1]且j-k >= 1)相同。如果k有多个,则取最大的一个。模式串t中每个位置j的字符都有这种信息,采用next数组表示:next[j] = MAX{ k }。
解释说来,当j = 0,next[0] = -1;
当j=1,串t在1 ~ j-1上没有字符,属于其他情况,next[1] = 0;
当t[k] = t[j],说明t[0],t[1],… ,t[k] = t[j-k],t[j-k+1],… ,t[j],则next[j] = k,next[j+1] = k+1;即0往后,j-k往后寻找相同字符的个数为k(包括0和j-k,但不包括k和j);
当t[k] != t[j],那么要寻找t[j]前一个长度更短的子串和开头字符起的子串相同,设k1 = next[k],若t[k1] = t[j],即0往后,j-k1往后寻找相同字符的个数为k1,则next[j]=k1;否则依此类推寻找更短的子串,直到next[j+1] = 0,因为next[j+1] = k1+1,k1+1是子串的长度。
求模式串t的next数组,算法如下:
void GetNext(SqString t,int next[])
{
int j,k;
j=0,k=-1; //j扫描串t,k记录t[j]之前与t开头相同的字符的个数
next[0]=-1;
while(j<t.length-1)
{
if(k == -1||t.data[j] == t.data[k])
{
j++;
k++;
next[j]=k;
}
else
{
k=next[k]; //k回退
}
}
}
2.
KMP算法的模式匹配过程
next表可以用来消除主串指针的回溯,减少匹配次数。
以目标串s = ”aaaaab”,模式串t = ”aaab”为例,第一次匹配从i= 0,j = 0开始,失配处为i = 3,j = 3,尽管匹配失败,也得到部分匹配信息,s[1]s[2]= t[1]t[2],next[3]=2。此时第二次匹配可从i = 3,j = next[3] =2开始,即主串指针i不变,模式串t右滑 j – next[j] 个位置,再进行比较。
KMP算法如下:
int KMP(SqString s,SqString t)
{
int i=0,j=0,next[Maxsize];
GetNext(t,next);
while(i<s.length&&j<t.length)
{
if(j==-1||s.data[i]==t.data[j])
{
i++;
j++;
}
else
{
j=next[j]; //i不变,j后退,模式串右滑
}
}
if(j>=t.length)
{
return (i-t.length); //匹配成功
}
else
{
return -1; //匹配失败
}
}