初学,理解可能不是那么准确~~
Next数组的含义:next[i]表示第0个元素到第i个元素组成的字符串的最大前缀后缀。Next[0]=0显然。
所以KMP的原理就是
通过找出每一阶段最大的相等的前缀后缀,那么匹配到某个字符失配时就可以从前缀的下一个字母开始匹配,而不用再回退匹配。使复杂度降从O(m^n)到O(m+n)。
最重要的就是求next数组,而且next数组在很多题都有应用,不止KMP。
而得到next数组的原理如下:
(画风就是这么洒脱,没办法~~~)
如图,如果比较到q,如果q点元素和q点元素相等,那么直接S1的长度加1就是next[q]了,即next[q]=next[q-1]+1。
但是如果不等,就要在S1中找到一个短一点的字符串,如棕色的那段,那样,因为黄色的那段S2和S1是相等的,那么在S1中找到的最长前缀后缀也和S2的后缀相等,这时,如果A点那里和q相等,那么next[q]就是棕色的那段长度+1喽~如果不相等,再向前减,对,这个过程就是代码里那里【k=next[k];】。如果到紫色的那里B和q相同,那么next[q]为紫色的长度+1.如果一直不相等,只能是0喽~
代码如下(代码原网址链接):
void makeNext(const char P[],int next[])
{
int q,k;//q:模版字符串下标;k:最大前后缀长度
int m = strlen(P);//模版字符串长度
next[0] = 0;//模版字符串的第一个字符的最大前后缀长度为0
for (q = 1,k = 0; q < m; ++q)//for循环,从第二个字符开始,依次计算每一个字符对应的next值
{
while(k > 0 && P[q] != P[k])//递归的求出P[0]•••P[q]的最大的相同的前后缀长度k
k = next[k-1];
if (P[q] == P[k])//如果相等,那么最大相同前后缀长度加1
{
k++;
}
next[q] = k;
}
}
但很时候next数组的意义是:next[i]表示第0个元素到第i-1个元素组成的字符串的前缀后缀,也就是如果把上面讨论的数组定义为nexta的话,那么nexta[i]=next[i+1] , 即nexta[0]=next[1]……nexta[n-2]=next[n-1],并定义next[0]=-1。
代码如下:
void GetNext()
{
//这个代码的原理和上差不多,就是改的短了些
int j = 0, k = -1;
Next[0] = -1;
while (j <= slen)
{
if (k == -1 || T[j] == T[k])
{
Next[++j] = ++k;
}
else
k = Next[k];
}
}
int KMP_Index()
{
int i = 0, j = 0;
GetNext();
while (i < slen && j < tlen)
{
if (j == -1 || S[i] == T[j])
{
i++;
j++;
}
else
j = Next[j];
}
if (j == tlen)
return i - tlen;
return -1;
}
也可以求一个字串出现的次数,代码稍作改动即可:
int KMP_Count()
{
int ans = 0;
int i = 0, j = 0;
GetNext();
while (i != slen && j != tlen)
{
if (S[i] == T[j] || j == -1)
++i, ++j; //第一次发现还可以这样写~
else
j = Next[j];
if (j == tlen)
{
++ans;
j = Next[j];
}
}
return ans;
}