kmp算法—你还是曾经那个算法没有一点一点改变,只是我现在变得很菜(算法回顾留着复习)

       昨日刷leetcode突然刷到一个最长回文字符串的题,在题解看到了马拉车算法,看完之后ohohohohoohohohoh!!!这个算法的优点就是记录已经获得的有用信息,减少无用的遍历。看完这个算法决定写个博客,所以我写了这篇Kmp算法(同样通过增加空间复杂度减少时间复杂度)

Kmp算法是用来寻找一个字符串是否包含另一个字符串,这个另一个字符串就是模式串

Kmp算法优秀在哪????

朴素算法BFKmp算法

在对主串的匹配的时候

主串:s1s2s3s4...sn

模式串:p1p2p3p4

转图一

 

kmp算法则

//当i=1时进行匹配,当匹配到i=4失败了就要从i=4开始
  //这就意味者i=2,i=3进行的匹配没有白费工夫了

 

 

图一:::::

//主串为s
//模式串为p
for(int i=0;i<s.size();i++)
{

    for(int j=0;j<p.size();j++)
        {

            if(j==p.size())
                return true;
            if(s[i+j]!=p[j])
                  break;          //当i=1时进行匹配,当匹配到i=4失败了就要从i=2开始
                                   //这就意味者i=2,i=3进行的匹配就白用了

        }

}

 

下面我用一个例子来表示为什么出现kmp算法。

比如 

             1 2 3 4 5 6 7 8 9 10 11 12  13

主串:   a b a b c d a b a b   a    b   e

模式串:a b a b e   

当我们进行匹配的时候1,2,3,4都时对的,当进行5的时候发现不相等,按照正常来说我们要会到主串的2进行重新匹配,但是我们肯定还会匹配到5这个位置,那我们为什么不将5这个位置直接对齐模式串的一个位置,使接下来的匹配直接从5开始从而不需要再对主串2、3、4进行判断。

这里说一下为什么能从5直接开始,因为首先模式串匹配到5这个位置就说明答案(就是最后要找到的字串)肯定包含5这个位置的字符串或者答案字符串在5这个位置后面,所以我们肯定能在模式串中找到一个位置,保证选择这个位置对主串的5的位置进行匹配的时候:

1.如果答案包含5,那么肯定能进行匹配成功

2.如果答案不包含5,那么也不会对后面产生影响

所以我们在找这个位置的时候一定是假设答案串包含5这个位置的,

因为我们找位置的时候把这个位置当成答案,所以进行匹配选择的这个位置之前的字符要匹配

就意思模式串中和c对标的这个字符之前的一定要和主串中的字符相同

回到我们的例子就是这样

主串:   a b a b c d a b a b   a    b   e

模式串:     a b a b e

但是如果有多种可能进行匹配呢,比如

             1 2 3 4 5 6 7 8

主串:   a b a b a b a c

模式串:a b a b a c

这时候第6个位置不同,我们要从第6个位置进行匹配,

可以选择

1.

主串:   a b a b a b a c

模式串:     a b a b a c

2.

主串:   a b a b a b a c

模式串:            a b a b a c

我们可以看到这两种情况,对于之前我们说的匹配的位置之前的字符都相同

1   中相同的aba

2   中相同的a

这时候我们如何进行选择呢

答案是选择第一种进行匹配,我们选择策略就是选最长的进行匹配,因为这样保证了如果匹配它一定是最优的匹配(意思就是1号能匹配成功但是2号不一定能匹配成功,但是如果2号能匹配成功,那么1号匹配不成功的话,那么我们会继续对匹配不成功的位置进行找模式串的位置,此时找到的最长的匹配就肯定是2号匹配,,这样选择1号可以是所有可能成为答案的子串都覆盖,而一开始选择2号则会丢失一些情况

 

所以问题到了现在我们找的就是如何找到模式串中的相应位置g,使模式串中的第g个字符去对标主串中我们刚才没有匹配成功的那个字符。使其对标后我们要满足两个条件:1、模式串g前面的字符和主串中对标位置上的字符相同。2、这个g的位置一定是尽可能大,意思就是g之前的字符组成的串是最长的

然后我们从例子始解决这个问题

主串:   a b a b a  b a c

模式串:a b a b a c

我们从头开始进行匹配,当我们进行到标注的这个字符的时候我们知道匹配不成功了,我们记主串中这个标记位置为r,这时候我们根据上面的分析,我们要找到模式串中的一个特殊位置g,来让这个位置g对标到主串中的标记字符(也就是上一次匹配没有成功的位置字符)。上面我们说到了我们找到的这个位置g达成在模式串中0~(g-1)位置上的字符都要和主串对应。

这时候我们要知道我们已经获得的信息是主串位置r之前的字符已经匹配了,放到这个例子中就是我们已经知道这标红的已经相等,所以这时候模式串中的标红字段可以替换主串中的标红字段了,所以我们要找目标就可以替换成去模式串中去找(为什么要这么替换呢,因为主串中标红字段前面可能有别的字符,这样会导致我们记录的信息过多,而因为模式串每次标红都是从第一个字符开始,所以更加方便规划)

 

 

 

我们为了找规律所以我们先从位置g最小开始找起

g=0

主串:   a b a b a  b a c

模式串:               a b a b a c

这时候我们满足g之前的和主串中都相等,如果g不能更大,那么g=0

g=1

主串:   a b a b a  b a c

模式串:            a b a b a c

这时候我们满足g之前的和主串中都相等,如果g不能更大,那么g=1,这时候g=0已经舍弃

g=2

主串:   a b a b a  b a c

模式串:         a b a b a c

这时候我们满足g之前的和主串中不相等,如果g不能更大,那么g=1

g=3

主串:   a b a b a  b a c

模式串:      a b a b a c

这时候我们满足g之前的和主串中都相等,如果g不能更大,那么g=3,这时候g=1已经舍弃

g=4

主串:   a b a b a  b a c

模式串:   a b a b c

这时候我们满足g之前的和主串中不相等,如果g不能更大,那么g=3

g=5

主串:   a b a b a  b a c

模式串   a b a b a  c

这时候我们满足g之前的和主串中不相等,g不能更大,所以g=3

所以只有在g=1和g=3时候符合相等条件,我们前面又说过可以用模式串中的去替换,所以当g=1和g=3是就可以转换为

g=1

  模式串 :   a b a b a c

g=3

模式串 :   a b a b a c

                        ⬆这个位置是又红又橙

 

 

所以我们要找的就转化为在第一次匹配失败时

主串:   a b a b a  b a c

模式串:a b a b a c

模式串中字符c(就是和主串匹配失败的字符)之前的字符组成字符串的前缀和后缀的最长公共部分。

前缀:包括首字符的除了最后一个字符的所有子串。

后缀:包括最后一个字符的除了首的所有子串。

而这种找最长的匹配位置的方法,就是找前缀和后缀的最长公共部分。

 

(未完待续。。。)

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值