JAVA数据结构——KMP算法

一、KMP算法问题提出

  • 有一个字符串 str1=“BBC ABCDAB ABCDABCDABDE”
  • 对其进行判断,里面是否包含另外一个字符串 str2="ABCDABD"

二、KMP算法算法思想

2.1 具体移动步骤

  • 第一步:用str1的第一个字符和str2的第一个字符去比较,不符合,关键词向后移动一位
    在这里插入图片描述

  • 第二步:重复步骤一,继续向后移动
    在这里插入图片描述

  • 第三步:一直重复直到str1有一个字符与str2的第一个字符符合为止
    在这里插入图片描述

  • 第四步:接着比较字符串和搜索词的下一个字符,是否还是符合,符合直接比,不符合进行下一步
    在这里插入图片描述

  • 第五步:此时如果继续重复步骤一,会出现“暴力匹配”,也就是继续一个一个往下比较,KMP算法思想就是利用已经知道的信息,不要把"搜索位置"拉回到以前,而是继续后移

在这里插入图片描述

  • 第六步:由第五步,我们可以了解到,其实我们进行第四步的时候已经比较过 ABCDAB 后面这些公共部分,所以对str2计算出一张一个叫 部分匹配的概念。

在这里插入图片描述

  • 初步观察,重复的部分的值出现的部分有多少个共同字母一次后面值就进行加多少,
  • 如:A出现过两次,则后面的A进行加1,
  • 如:AB出现过一次,且AB拥有两个字母则加2
  • 实际上:公式:移动位数 = 已匹配的字符数 - 对应的部分向后移动的位数
  • 由此,我们再进行一次移位操作
  • 未经过算法移动(仅仅向后移动一位):
    在这里插入图片描述
  • 经过算法移动(向后移动直到部分相同的):

在这里插入图片描述

在这里插入图片描述

  • 继续移动(使用算法)
    在这里插入图片描述
  • 继续移动(使用算法)
    在这里插入图片描述

2.2 部分匹配表

  • 由上面的步骤,我们有一个问题,就是怎么样去确定到底应该向后移动多少步骤,由此引出部分匹配表的概念

  • 前缀后缀

    • 如字符串:"bread"
    • 前缀: b,br,bre,brea
    • 后缀:read,ead,ad,d
  • 部分匹配表

    • 就是“前缀”和“后缀”的 最长的共有元素的长度
    • 如:“ABCDABD”
      • ”A”的前缀和后缀都为空集,共有元素的长度为 0;
      • ”AB”的前缀为[A],后缀为[B],共有元素的长度为 0;
      • ”ABC”的前缀为[A, AB],后缀为[BC, C],共有元素的长度 0;
      • ”ABCD”的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为 0;
      • ”ABCDA”的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA,A],共有元素为”A”,长度为 1;
      • ”ABCDAB”的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为”AB”,长度为 2

在这里插入图片描述

三、KMP算法JAVA代码实现

public class KMPAlogrithm {

    public static void main(String[] args) {
        String str1 ="BBC ABCDAB ABCDABCDABDE";
        String str2 ="ABCDABD";
        //获取到一个字符串(字串)的部分匹配值
        int []next =kmpNext("ABCDABD");//[0,1]
       	int result = kmpSercach(st1,str2,next []);
       	System.out.println("result = "+result);
    }
    //写出kmp搜索算法
    /*
    * str1 原字符串
    * str2 子串
    * next 部分匹配表
    * @return 如果是-1,就是没有匹配到,否则返回第一个匹配的位置
    * */
    public static int kmpSerach(String str1,String str2,int[] next){
        //遍历
        for (int i = 0,j=0; i < str1.length(); i++) {

            //需要处理,当str1.charAt(i) != str2.charAt(j)
            //kmp算法核心点
            while ( j > 0 &&str1.charAt(i) != str2.charAt(j)){
                j = next[j -1];
            }
            if (str1.charAt(i) == str2.charAt(j)) {
                j++;
            }
            if (j == str2.length()){
                return i - j - 1;
            }
        }
        return -1;
    }

    public static  int[] kmpNext(String dest){
        //创建一个next数组保存部分匹配值
        int[] next = new int[dest.length()];
        next[0] = 0;//如果字符串是长度为1部分匹配值就是0
        for (int i = 1,j=0; i < dest.length(); i++) {
            //当dest.charAt(i)!=dest.charAt(j),需要从next[j-1]获取新的j
            //直到发现dest.charAt(i) ==dest.charAt(j)成立时候退出
            //kmp算法核心点
            while ( j > 0&&dest.charAt(i)!=dest.charAt(j)){
                j=next[j-1];
            }

            //当这个条件满足时,部分匹配值就加1
            if (dest.charAt(i)==dest.charAt(j)){
                j++;
            }
            next[i] = j;
        }
        return next;
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值