算法思路
用i来标记S主串匹配位置,j来标记T子串匹配位置,采用i不回退,j回退到nxt[j]位置的方式降低匹配时间。
其中,nxt[j]数组表示在j位置匹配失败最优的回退位置,即S[0]~S[i-1]在T[0]~T[j-1]之间除了当前串最长的匹配子串的位置
求nxt数组
nxt数组实际上就是T的前缀与S后缀的最长匹配长度,由于匹配过程中S[i-j]~S[i-1]与T[0]~T[j-1]完全相等,因此只需要搜索T的子串T'(T[0]~T[j-1])中前缀和后缀相等的最大长度即可。
在前缀和后缀相等的最大长度的查找中,利用递推思想简化复杂度
假设已经知道nxt[j]=k
①如果T[j]==T[k](注意,处理到某个位置时代表的是前面的位置相等,当前位置不一定相等)
显然nxt[j+1]=k+1
②如果T[j]!=T[k]
k=nxt[k]
nxt[k]相当于直接跳到了上一个与当前已匹配子串相同的位置,直到T[j]==T[k]为止
void get_nxt(string t)
{
int j=0,k=-1,tlen=t.length();
nxt[0]=-1;//初始化
while(j<tlen)
{
if(k==-1 || t[k]==t[j])//k==-1一定要写前面不然会下标越界
nxt[++j]=++k;
else
k=nxt[k];
}
}
int KMP(string s,string t,int pos)
{
int slen=s.length();
int tlen=t.length();
int i=pos,j=0;
while(i<slen && j<tlen)
{
if(j==-1 || s[i]==t[j])
i++,j++;
else
j=nxt[j];
}
if(j>=tlen) return i-tlen+1;
else return -1;
}
第①步有一个优化
如果T[j+1]==T[k+1]
那么nxt[j+1]=nxt[k+1]
因为如果T[j+1]==T[k+1]那么这一次回跳的比较肯定是失败的,因此直接跳到下一个位置
该优化不降低复杂度级数,仍为,但减少了一定的比较次数。
void get_nxt(string t)
{
int j=0,k=-1,tlen=t.length();
nxt[0]=-1;//初始化
while(j<tlen)
{
if(k==-1 || t[k]==t[j])//k==-1一定要写前面不然会下标越界
{
if(t[++j]==t[++k])
nxt[j]=nxt[k];
else nxt[j]=k;
}
else
k=nxt[k];
}
}