KMP算法的个人理解

KMP算法的个人理解

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 。

这个就是最经典的kmp题目,两个字符串,求第一个匹配项。

正常的想法都是双指针。从左到右一个个匹配,如果这个过程中有某个字符不匹配,第一个指针会到原位置,第二个指针移到第0位。

上面的程序是没有问题的,但不够好!

如果是人为来寻找的话,肯定不会再把i移动回第1位,因为主串匹配失败的位置前面除了第一个A之外再也没有A了,我们为什么能知道主串前面只有一个A?因为我们已经知道前面三个字符都是匹配的!(这很重要)。移动过去肯定也是不匹配的!有一个想法,i可以不动,我们只需要移动j即可
原文链接:https://blog.csdn.net/weixin_52622200/article/details/110563434

思路其实也很简单,我们的双指针算法问题在于,每次第一个指针都要回到原来的位置,那能不能第一个指针不动,当第一个指针与第二个指针指向的字符不一致的时候,第二个指针动。

​ i

ABABABCAA

ABABC

​ k

当i和k指针指向的数据不一致时候,i不要变为2,而是k变成2.

​ i

ABABABCAA

​ ABABC

​ k

这样我们就可以继续对比下去了,只要k什么时候变成第二个字符串的长度,就说明中了。

为什么能这么变呢。

我们把ABABC分为两部分,可以知道ABAB和C。 我们在匹配到C的时候,我们一定知道第一个字符串形式应该是

XX ABABX MM

我们已知的信息就说这段是ABAB的,那我们第二字符串就可以从第二开始对比,为什么,因为第二字符串的头AB和第一个字符串的AB相同,换句话说,就说ABAB的前后共同部分长度为2.

那么我们只要知道第二个字符串每个长度的共同最大长度数据next[]就可以了。

后面他们说的就很清楚了,就是当求解next算法的时候,他们的写法看起来很优美,但是我写不出来,理解不了啊。

那怎么办呢。看了b站的动画视频后,我就理解了。

我们求解下ABACABAB的next数组。

我们自然就想到从头开始。A 那自然是0,那AB呢,我们人眼看出来也是0,但是机器不知道啊,难道我们要头尾指针,再算一遍吗,可以是可以,但是不应该啊,每个长度都头尾一下,也很麻烦啊。

我们自然就想到,第一步是不是包含了第二步的信息。

现在暂时看不出来,那继续。

ABA 1

ABAC 0

ABACA 1

ABACAB 2

这里就有点感觉了,为什么ABACAB是2,我们人知道,因为ABACA是1,所以我们只要比较ABACA的第二个位置B和我们最后的ABACAB的B是否一致。是的话,我们就是1+1.

这里我们就可以知道,如果字符串的next[i-1位置的字符串和i位置一致,那么next[i]=next[i-1]+1;

但是如果不一致的话, 我们该怎么处理呢。

我们继续下去,

ABACABA 3

ABA |C ABA |B 这个我们是怎么算的呢C与B不一致,那么是要从头算吗。

不一定哦

我们知道ABACABA 是3 那么说明里面有一段是共同的,就是ABA|C|ABA

那我们ABA这段呢,A|B|A

右边的后缀=左边的后缀,同事左边的一部分后缀=左边的一部分前缀,所以,应该存在一个左边的前缀=右边某一部分后缀。那么自然A|BACAAB|A ,那么这个时候,就检查,第一个A的下一个B是不是跟最后的B相同,如果相同就是1+1.如果不同那再去更小的前缀相同的进行比较。

 public static int strStr(String haystack, String needle) {
        if(needle==null || needle.length()==0) return 0;
        if(haystack==null || needle.length()==0) return -1;
        int alength = haystack.length();
        int blength=needle.length();
        char[] haystackA = haystack.toCharArray();
        char[] needleA = needle.toCharArray();
        int[] next = getnext(needle);

        int posA=0;
        int posB=0;
        while(posA<alength){
            if(haystackA[posA]==needleA[posB]){
                posA++;
                posB++;
            }else if(posB>0){
                posB=next[posB-1];
            }else{
                posA++;
            }
            if(posB==blength){
                return posA-posB;
            }

        }
       return -1;
    }

    public static int[] getnext(String target) {
         int length = target.length();
        int[] next = new int[length];


         int i=1;
         int prefix_len=0;
         while(i<length){
             if(target.charAt(prefix_len)==target.charAt(i)){
                 prefix_len=prefix_len+1;
                 next[i]=prefix_len;
                 i++;
             }else{
                 if(prefix_len==0){
                     next[i]=0;
                     i++;
                 }else{
                     prefix_len=next[prefix_len-1];
                 }
             }
         }
        return next;
    }

那些算法为什么看起来这么优美,简单高效。因为是经过了优化的。但是缺点就是因此失去了语义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值