前言
参考B站奇妙编程学院的动画,笔者使用Manim重新设计了与其类似地动画
KMP算法的过程
这里假设SString第一位存储字符串的长度
c
int kmp(SString S, SString T, int pos) {
int* next = buildNext(T);
// 假设用第一位存储字符串的长度
int i = pos; j = 1;
while(i <= S[0] && j <= T[0]) {
if(S[i] == T[j]) {
++i; ++j;
} else {
// 模式串的字符部分从1开始,而对应的next从0开始
if (next[j-1] > 0) { j = next[j-1] + 1; }
else { j = 1; ++i; }
}
}
if(j > T[0]) return i - T[0];
else return 0;
}
请见B站的动画,已经很详细了
Next数组的构建过程
这里假设SString第一位存储字符串的长度
c
int* buildNext(SString T) {
int next[T[0]];
// 第一个字符没公共前后缀
next[0] = 0;
int len = 0; // 左边子串的最长公共前后缀长,在+1后也能表示左侧子串后侧第一个元素的索引
int i = 2; // 从第二个字符开始
while(i <= T[0]) {
/**
初始条件:当前子串T[1], T[2]
左侧子串原来是空的,后第一个字符T[len + 1]= T[1]
右侧子串原来空的,后第一个字符T[i] = T[2]
假设其相等,则当前子串一共有两个字符,有公共前后缀T[1]/T[2],公共前后缀+1,扔next里对应T[2]的位置里
以后: 当前子串T[1],T[2]...T[len],T[len+1] | T[k],T[k+1],...T[k+i-1],T[i]
左侧子串为T[1],T[2],T[len],后面的第一个新字符为T[len+1]
右侧子串为T[k],T[k+1],T[i-1],后面的第一个新字符为T[i]
假设其相等,则当前子串的的最长公共前后缀+1,扔next里对应T[i]的位置里
**/
if(T[len + 1] == T[i]) {
++len;
next[i-1] = len;
++i;
/**
T[1],T[2]...T[len],T[len+1] | T[k],T[k+1],...T[i-1],T[i]
如果当前子串的左侧子串右侧新的字符T[len+1]和右侧子串右侧的新的字符T[i]不相等
这时如果左边能滑动(next[len] != 0),则右滑左边,接着反到新一轮循环,看后一个字符是否相等
**/
} else if (next[len] > 0) {
len = next[len];
/**
如果右滑不了则没有公共前后缀,直接置零
**/
} else {
next[i - 1] = 0;
++i;
}
}
return next;
}
首先考虑while循环内第一个if成立时的“初始条件”
在此时T内只有两个相同的元素,我们考虑将其分别称为“左侧子串”和右侧子串,比较后获得next数组的值
而如果二者不相等,考虑代码下面else if中的内容
否则再全部置0
全部评论 (0)
暂无评论,快来抢沙发吧~