KMP算法

KMP

俗称看毛片算法:

kmp算法是一种字符串匹配算法,用于模式串和主串之间的关系

暴力算法(幼稚匹配算法

  1. 每次匹配模式串的第0到第i个字符串,一直到匹配成功或者出现了不一样的字符

  2. 不一样字符串的时候,窗口向前移动一位,然后右指针回溯到初始位置重新匹配

KMP算法

建议搭配视频使用

暴力解每次的右指针回溯,让我们中间匹配的过程全 部 木 大,难道我们一直以来的努力都全部白费吗?于是便有了kmp算法

我们可以想想,当右指针出现不匹配的时候,窗口内的值是不是已经全部匹配了,那这部分的值真的毫无作用吗,如果能提前判断好模式串的结构,能不能不回溯右指针,而是反过来移动窗口。于是便有了kmp(虽然并不是这样出来的)

核心:kmp算法省去了右指针的回溯过程(所以时间复杂度是o(m+n)),通过提前对模式串的结构进行分析,在匹配的时候直接移动窗口而不是移动右指针。

理论基础:前缀,后缀,以及前后缀匹配值。

  • 前缀:从第0个字符到第i个字符的字符子串 (不包含最后一个字符)

  • 后缀:从第i个字符到第n个字符的字符字串 (n为字符长度)(不包含第一个字符)

  • 前后缀匹配值:即前缀和后缀的相同部分

我们提前对模式串的每个位置的前后缀结构进行分析。

比如字符串:"abcaba"

  • 对于位置1(从1开始,0用于放其他东西),只有'a',所以其前后缀均为空集

  • 对与位置2,”ab“,很容易看出其前后缀匹配值也是空。

  • 对于位置3:”abc“,同理

  • 位置4,”abca“,其中前缀”a“,和后缀”a“出现重合,那么位置四对应前后缀匹配值就是”a“(TIPS:我们通常仅仅记录前后缀匹配的最长长度,因为我们移动的时候只要知道距离就行)

  • 位置5:”abcab“,容易看出前缀“ab”,后缀“ab”相同,那么最长长度就是2

  • 位置6:最后一位,我们不做记录(原因之后会有)

好了,你现在已经掌握了必要理论(大概

那么这个理论又有什么用呢

图来自上面的视频

对于上图这样的情况,我们已经提前知道了指针处左侧的字符串的结构,他的后缀和前缀有两位匹配,那么我们直接将窗口前缀的“AB”移动到指针左边的那个“AB”处,然后继续移动指针。

 

 

这样就不会出现右指针的回溯了(跳过的元素中并不会出现漏掉的答案,因为如果出现漏掉的答案那么其会有和模式串相同的结构,这部分的信息已经被我们提前用前后缀匹配过了,并不会出现这样的情况,这里有点难懂,想想应该能明白,没图画给你了)

那么对于模式串的结构,我们要怎么求出来呢。(我们用next[]存储每个对应位置的最长前后缀匹配长度)

如果你根据上面所说的方法求解,你会发现最后又变成了暴力求解。

因为对于每一个位置,求解前后缀匹配长度,都需要O(n)的时间,那么n个位置,就是O(n2)那么这个算法就失去了原本的意义(即使是这样,也比暴力快)

对于next[]数组

next数组求法

以下图片也来自于此视频

//将对于s的next数组生成放到next里面
int GetNext(string& s, int next[], int size) {
    next[1] = 0;
    int i = 1, j = 0;
    while(i < length){
        if(j == 0 || ch[i] == ch[j]) {
            next[++i] = ++j;    //i++;  j++;    next[i] = j
         }else{
            j = next[i];
         }
    }
}

假设我们要求解,next[17],那么我们一定已经知道了next[16],那么由next[16] = 8,我们可以知道所框部分一定是相同的。那么此时只要看看ch[8]和ch[16]是否相等即可。

 

 

如果相等就是最好的情况,直接让next[17] = next[16] + 1即可

那么如果ch[16] != ch[8]呢,我们可以再分,把1-7分成两部分

 

这里我们再分,就有1-3起码是一样的所以只要判断ch[4] ?= ch[16]

这里数字取的都比较巧妙,建议自己用其他数字再演算一边加深印象

 

我们可以仔细思考下kmp算法的极端情况,如果next数组一个前后匹配的都没有呢,那么就是每次窗口移动一格,此时我们也能发现暴力其实也是一次移动一格(因为最开始就出现了不匹配),这个时候速度相当,(暴力还快些,因为不需要提前处理)

所以其实如果模式串结构不明显,或者主串中很少出现部分匹配的话,暴力算法和kmp算法的在时间上相差并没有那么多。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值