深入理解并分析KMP算法——KMP:解决所有的包含问题!

说KMP前说一下String类型的indexOf方法:

  1. indexOf所用到的底层是KMP算法大体上类似,但是在KMP的常数项上进行了进一步优化,使之更加完美。KMP自身已经很牛了,开淦!

KMP所解决的是一个包含问题

  1. 核心技术点在于一个next数组的创建和使用过程
  2. 先说next数组的创建吧
    1. next数组的0位置人为规定-1;
    2. next数组的1位置认为规定0;
    3. next数组所记录的是目标数组的当前索引前(不包括当前索引)最长相等的前缀和后缀
      1. 举个例子
      2. 由上图得知索引位位x的最长前后缀的长度为4;
    4. 先来个开胃菜,展示一下如何手算最长前后缀。(附带分析过程)
    5. 再说如何机算KMP的next数组
      1. 前提是一样的
        1. next[0]是-1
        2. next[1]是0
      2. 机算运用到了当前索引的前一个位置的next数组中的值;大家只用记住规则就好,到时候根据规则就可以记住代码!
        1. 如果next数组中当前索引的前一个的值所对应的字符 = 当前索引的前一个字符:str.charAt(next[index - 1]) == str.charAt(index-1);
          1. 等于的话
            1. next[index] = next[index-1]+1;
          2. 不等于的话:(这里存在一个向next数组左边推并对比的一个过程)
            1. str[next[index-1]] 与str[index-1]再进行对比,一直这样对比,如果相等了,next[当前] = next[当前推到的位置+1];
        2. 这块需要在字符串数组和next数组这颠倒,若上面的理论不明白,可以直接配合代码食用
          1. //判断要求的索引的坐标要求的next数组是否一样
                private static int[] getNextArr(String s) {
                    int[] next = new int[s.length()];
                    next[0] = -1;
                    next[1] = 0;
                    //开始计算next数组
                    a:
                    for (int i = 2; i < s.length(); i++) {
                        //记录住始终变得信息
                        int j = next[i - 1];
                        //如果不相等
                        b:
                        while (j > 0 && s.charAt(i - 1) != s.charAt(j)) {
                            //此时代表不相等,坐标后移
                            int index = next[j];
                            j=next[j];
                            if(s.charAt(j) == s.charAt(i-1)){//如果相等了
                                next[i] = index+1;
                                continue a;
                            }
                        }
            
                        //如果相等
                        if (s.charAt(i - 1) == s.charAt(next[i - 1])) {
                            next[i] = next[i - 1] + 1;
                        }
                    }
                    return next;
                }

             

next数组出来后,就到了next数组的使用了 

  1. 整体是一个记录有几个位置已经和目标数组一样了的关系。
    1. 如果当前字符串的索引和目标数组索引字符相等,相等数量+1,直到相等的数量完全相等,返回当前字符串数组index的位置-目标数组的长度+1;
    2. 如果当前字符串的位置和目标数组的位置的字符不相等,让目标数组的  next[当前位置的前一个的坐标]+1位置  与  字符串数组接着比较
      1. 同时这里有一个证明 str中的next[index-1]之前的位置证明不出目标字符串
      2.  

接下来看全部代码

package LiKou.KMP;

import java.util.Arrays;

public class ZUOKMPTest {
    public static void main(String[] args) {
        //String s1 = "BBC ABCDAB ABCDABCDABDE";
        String s1 = "BBC ABCDABAFABCDAB ABCDABCDABDE";
        String s2 = "ABCDABAF";
        int[] next = getNextArr(s2);
        System.out.println(Arrays.toString(next));
        int index = MyKMP(s1, s2, next);
        System.out.println(index);
    }

    private static int MyKMP(String s1, String s2, int[] next) {
        for (int i = 0, j = 0; i < s1.length(); i++) {
            while (j > 0 && s1.charAt(i) != s2.charAt(j)) {
                j = next[j - 1]; //在这是一个不断迭代的过程
            }

            if (s1.charAt(i) == s2.charAt(j)) {
                j++;
            }
            if (j == s2.length()) {
                return i - j + 1;
            }
        }
        return -1;
    }


    //判断要求的索引的坐标要求的next数组是否一样
    private static int[] getNextArr(String s) {
        int[] next = new int[s.length()];
        next[0] = -1;
        next[1] = 0;
        //开始计算next数组
        a:
        for (int i = 2; i < s.length(); i++) {
            //记录住始终变得信息
            int j = next[i - 1];
            //如果不相等
            b:
            while (j > 0 && s.charAt(i - 1) != s.charAt(j)) {
                //此时代表不相等,坐标后移
                int index = next[j];
                j=next[j];
                if(s.charAt(j) == s.charAt(i-1)){//如果相等了
                    next[i] = index+1;
                    continue a;
                }
            }

            //如果相等
            if (s.charAt(i - 1) == s.charAt(next[i - 1])) {
                next[i] = next[i - 1] + 1;
            }
        }
        return next;
    }

}

 今天就到这,突然发现没有做kmp的深度认识,下个博客,聊聊马拉车manacher

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值