理解关键:
1.时间复杂度o(m+n),主串不回溯,一直向前推进。模式串通过next[]表进行下标跳转
2.模式串next[]数组构造。
求解当前下标i的next[i]的关键是找出p[0]到p[i-1]字符串中,以p[i-1]结尾的最长子串,该子串满足条件p[0]~p[k-1]与p[i-k]~p[i-1]相同(即查找0~i-1的最长相同前后缀),则next[i] = k;
next表构造代码如下:
#define MAX_PATTERN_LEN 20
int next[MAX_PATTERN_LEN] = { 0 };
int makeNext(char* p)
{
next[0] = -1; next[1] = 0;
int i, k;
for (i = 1, k = 0; i < strlen(p);)
{
while (p[i] != p[k] && (k >= 0))
{
k = next[k];
}
i++;
k++;
next[i] = k;
}
printf("the next table:\n");
for (i = 0; i < strlen(p); i++)
{
printf("%c %d\n", p[i], next[i]);
}
return 0;
}
该算法最难理解的就是next[]数组回溯时,为什么是k=next[k], 而不是k=k-1这样挨着回溯:
回到算法的关键是找到模式串p[0]~p[i-1]子串的最长相同前后缀,而next[]表正是存储的0~i-1每个下标位置的相同前后缀,所以最长相同前后缀的值一定是next[]表中存储的值。
下面来通过图解来清晰的理解:
其中k = next[j], 则A,B,C都是有相同前(即以p[0]位头,以p[j-1]为尾)后缀的字符段,且长度逐减。
当j位置失配时,说明0 ~ j-1是匹配成功的,目标串不移动,如果目标串text的当前下标为i,则text[i-1]与模式串的p[j-1]匹配,问题就是要使模式串进行最小的移动, 即需要找到已经匹配成功的0 ~ j-1的字符串中以p[0]位头,以p[j-1]为尾的前缀,而满足该条件的0 ~ k -1, 0 ~ next[k] -1, 0 ~ next[next[k] - 1]......, 所以最长的就是0 ~ k-1(即红色的A1段), 即next[j] - 1。次长的就是0 ~ next[k]-1(即绿色的B1段), 以此类推,得出算法回溯时为什么k要递归的等于next[k]了, 即k = next[k]
本人菜鸟,该问题困扰我很久,花了很多时间来理解这个,网上的讲解看了很多,总感觉有点似懂非懂,现在终于感觉搞明白透彻了,按照自己的理解记录下来。如果有小伙伴也跟我一样看了很多还是不懂,希望我这篇能帮到你
参见链接:https://www.cnblogs.com/tangzhengyue/p/4315393.html