KMP 算法学习

本题 感谢大牛博客讲解: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 返回的 是不重复的情况最近的地点,所以有了这种性质,我们就可以拿他来判断循环节了。

猜你喜欢

转载自blog.csdn.net/qq_40924940/article/details/83929563