KMP算法

在讲述KMP算法前,首先要理解什么是部分匹配表。

1,部分匹配表

在这里插入图片描述

在这里插入图片描述

2,KMP算法

题目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ax3zXbzI-1649073178612)(%E7%AE%97%E6%B3%95%E5%B7%A6%E7%A8%8B%E4%BA%91.assets/1649072790407.png)]

思路分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cCKKGgVr-1649073178612)(%E7%AE%97%E6%B3%95%E5%B7%A6%E7%A8%8B%E4%BA%91.assets/1649072821802.png)]

为什么可以进行上面的这种写法呢?是因为我们已经知道了最大前缀和后缀了, 那么我们下一回找的时候完全可以直接将str移动到最大后缀的地方,省下了不少的空间。

算法实现

package kmp.zuochenyun;

/**
 * @version v1.0
 * @ProjectName: 数据结构
 * @ClassName: Code01_KMP
 * @Description: 请描述该类的功能
 * @Author: ming
 * @Date: 2022/4/4 18:14
 */
public class Code01_KMP {
    /**
     * 创建一个部分匹配表
     * @param ms
     * @return
     * 创建部分匹配表是通过动态规划的方式完成的
     * 1,规定,部分匹配表next的0和1的位置始终为-1和0
     * 2,之后的部分始终要用前面的部分匹配表来表示,例如
     *      数组 ms[] = abbstabbecabbstabb?
     *      现在我们要计算的是?的部分匹配表
     *      1,设一个指针i,指向?
     *      2,i-1的部分匹配表是8(8其实就是cn),这个时候可以分情况讨论
     *          2.1,如果?等于ms[8],就说明?的部分匹配表的值为next[i-1] + 1
     *          2.2,如果i-1的部分匹配表大于0且不符合2.1的情况,就说明我可以往前跳,跳到next[cn]的位置
     *               也就是e的位置,之后继续进行步骤2的判断讨论,e的next值是3,假设?是s,那么i的next就是4
     *          2.3,如果不符合2.1和2.2的情况,同时cn是不大于0的情况,也就是我们无法往前跳了,即前面没有
     *               一个和我相同的字符,这个时候i的部分匹配值就是0。
     */
    public static int[] getNextArray(char[] ms) {
        if (ms.length == 1) {
            return new int[] {-1};
        }
        int[] next = new int[ms.length];
        next[0] = -1;
        next[1] = 0;
        int i = 2;
        //cn表式指向和i-1比较的位置,也是当前位置的部分匹配表
        int cn = 0;
        while (i < next.length) {
            if (ms[i - 1] == ms [cn]) {
                /**
                 *下面的代码等同于
                 * cn++;
                 * next[i] = cn;
                 * i++;
                 */
                next[i++] = ++cn;
            } else if (cn > 0) {
                cn = next[cn];
            } else {
                next[i++] = 0;
            }
        }
        return next;
    }

    /**
     * @param s
     * @param m
     * @return
     */
    public static int getIndexOf(String s, String m) {
        if (s == null || m == null || m.length() < 1 || s.length() < m.length()) {
            return -1;
        }
        char[] str1 = s.toCharArray();
        char[] str2 = m.toCharArray();
        //是str1的指针
        int i1 = 0;
        //是str2的指针
        int i2 = 0;
        //获取部分匹配表
        int[] next = getNextArray(str2);
        while (i1 < str1.length && i2 < str2.length) {
            //如果相等就让指针都向后移动一位
            if (str1[i1] == str1[i2]) {
                i1++;
                i2++;
                //如果next[i2] == -1,也就是在第一个位置就不一样,那么就让i1指针向后移动一位
            } else if (next[i2] == -1){
                i1++;
                //否则就要让i跳到next[i2]上,进行一个加速。
            } else {
                i2 = next[i2];
            }
        }
        //如果i2越界,那么就说明我们已经找到了,他在s1的初始位置是i1-i2,如果i1越界,就说明不包含。
        return i2 == str2.length ? i1 - i2 : -1;
    }

    public static void main(String[] args) {
        String str = "abcabcababaccc";
        String match = "ababa";
        System.out.println(getIndexOf(str, match));

    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值