本题 感谢大牛博客讲解:https://www.cnblogs.com/yjiyjige/p/3263858.html
写的非常好,几个图片也来自于他的博客
首先简单谈一下 kmp ,由于昨晚做了一道题 涉及到了循环节这个概念,今天我在学习的过程中发现原来循环节是可以求的,时可以用一种算法求出循环节的长度的
这种算法需要涉及到 kmp 算法了。
kmp 即 想要在主字符串中找到匹配的 目标串 ,当然我们可以用最暴力的方法求解,意思大概如图解
从 str 1 [i] 与 str 2 [j] 逐个比较 如果不匹配,则返回起点后一位 继续开始遍历
返回 i-j+1继续
其大概流程就如上图
但是我们也发现了 这种方法实在太暴力了,我们应该优化一下这种方法,
从人的视角来看,一旦这个匹配失败了。。我们完全可以省去前几步,直接从“最为相似的”部分开始,即不需要在这样从头一点一点来搞了,可以直接跳到这一步
因为前 3 个已经匹配上了对吧 我们再错位比较毫无意义的,因为相同的字符 错位比较肯定不会一致 所以采用大牛的说法就是:
利用已经部分匹配这个有效信息,保持i指针不回溯,通过修改j指针,让模式串尽量地移动到有效的位置。
为什么我们这次会跳到这个位置呢 因为 前三位已经是完全匹配上的啊,如果错位比较是无用功,而且我们要求 i 不要移动,只需要移动 j 就可以了 既然 目标串中 A 和 主串中的 A 已经一致了,我们需要往后移动一位 j 而不是归零,下边那个案例也是
此处讲 j 移动到 2 因为 前两位一致
而这种跳跃有一定的规律的,不是纯粹的凭借肉眼看到的,下边是大佬总结的图解
可以总结一个公式
当T[i] != P[j]时
有T[i-j ~ i-1] == P[0 ~ j-1]
由P[0 ~ k-1] == P[j-k ~ j-1]
必然:T[i-k ~ i-1] == P[0 ~ k-1]
之后 我们就需要一个 next[] 数组来详细的记录一下 k , next[j] = k 这样记录后一旦遇到不匹配情况,可以直接借助 next 数组跳到我们最近的位置
简单介绍一下代码
char c[999999];
int len = strlen(c);
int next[999999];
void getnext(const char *c)
{
int i=0,j=-1;
next[0]=-1;
while(i!=len)
{
if(j==-1 || c[i]==c[j])
next[++i]=++j;
else
j=next[j];
}
}
我们发现一个规律:
当P[k] == P[j]时,
有next[j+1] == next[j] + 1
而不相等的时候 k = next[k] 其原理如下
之后就可以尝试着去写 kmp 了
KMP 写法 就看下一篇文章了。
总结一下 :
next 数组 最大的用处之一 就是做一个前缀数组,判断这种循环之前是否还出现过,如果出现过,就不需要再走一遍了,是对一个数组自己本身的处理,next 返回的 是不重复的情况最近的地点,所以有了这种性质,我们就可以拿他来判断循环节了。