KMP算法实现

KMP算法核心思想就是跳过已经对比过的字符,不进行重复对比,在代码中比较难以理解的其实就是下面这段代码:

//源字符串:str1     匹配字符串:str2
while (j > 0 && str1.charAt(i) != str2.charAt(j)) {
       j = next[j - 1];
}

上面这段代码是借助了匹配表(next数组)所实现的,同时这也是最难以理解的地方,但它也是KMP实现的核心;按照我的理解就是:当匹配字符串str2的 j 个位置的值与str1在 i 的位置的值匹配失败时,那么此时应该在next数组中找到 j 位置的前一个位置元素的值,也就是找到next数组中 j - 1这个位置的值,该匹配值告诉我们下一个与 str1.charAt(i) 需要对比的str2中的字符的下标,这就是它的核心所在,我也是通过画图推演,花了几天才弄明白,匹配值就是为了让我们忽略已经对比的过的字符,减少重复对比。


完整KMP代码实现以及思路:

package com.zc.study;

import java.util.Arrays;

/**
 * @author zc
 * 模拟KMP算法实现
 */
public class KMPDemo {
    public static void main(String[] args) {
        String str1 = "AAB AABAA BAABABCDABDA AB";
        String str2 = "ABCDABD";
        String str3 = "ABCDABAABAA BAABABCDABDA AB";
        String str4 = "ABCDABD";
        String str8 = "ABAB";
        /**
         * 思路:
         * 假设源字符串中的指针为i,匹配字符串中的指针为j;
         * 当匹配字符串和源字符串出现字符匹配失败时,
         * 获取匹配字符串中当前字符的前一个字符所在的next数组中的值,也就是next[j-1],让它等于j
         * j此时所代表的是当前需要对比的元素下标(这里比较难以理解)
         */

        String str6 = "ABABABCDABAA BAABABABABDA AB";
        String str5 = "ABABABD";

        System.out.println(Arrays.toString(matchTable(str8)));
        System.out.println(kmpSearch(str6, str5));
    }

    /**
     * @param str1:源字符串
     * @param str2:匹配字符串
     * @return 返回匹配字符串在源字符串中的开始索引
     * */
    public static int kmpSearch(String str1,String str2){
        //计算得出匹配表
        int[] next = matchTable(str2);
        //i是str1中的字符指针,j是str2中的字符指针,字符串的对比是通过两个指针来进行的
        for (int i = 0,j = 0; i < str1.length(); i++) {
            //当源字符串与匹配字符串不相同时,通过匹配表的规则,获取到当前指针的上一个位置:next[j - 1],然后将源字符串的当前字符与str2.charAt(next[j - 1])做对比,如果一直不同,就一直重复进行对比,直到j=0,也就是指针指向了匹配字符串的第一个位置为止
            while (j > 0 && str1.charAt(i) != str2.charAt(j)) {
                j = next[j - 1];
            }
            //对比成功,指针j后移,继续比较两个字符串中的下一个字符
            if(str1.charAt(i) == str2.charAt(j)){
                j++;
            }
            //证明匹配成功,str1中包含了str2,返回下标
            if(j == str2.length()){
                //为什么索引位置是i + 1 - j,因为数据元素一共有 i+1 个,下标为0的数据元素也是其中之一,如果直接i-j就会少计算一个数据元素的个数
                return i + 1 - j;
            }
        }
        //没有找到指定字符串
        return -1;
    }



    /**计算目标字符串匹配表
     * 匹配表中每个元素的值实际代表的是匹配字符串每个字符的索引
     * */
    public static int[] matchTable(String str){
        int[] next = new int[str.length()];
        //当字符串只有一个字符时,公共前后缀的长度必定为0,所以第一个元素的值设定为0
        next[0] = 0;

        for (int i = 1 , j = 0; i < next.length; i++) {
            //当字符不相同时,将当前next[j]的上一个位置的值赋值给j,即 j = next[j - 1],此时j的值正是匹配字符串所需要移动的步长,要就是为了防止重复对比,下次直接需要对比的匹配字符串元素下标;然后比较str.charAt(i) 与 str.charAt(j)是否相等
            while (j > 0 && str.charAt(i) != str.charAt(j)) {
                j = next[j - 1];
            }
            //字符相同时,匹配值j+1
            if(str.charAt(i) == str.charAt(j)){
                j++;
            }
            //将位置i的匹配值设定为j
            next[i] = j;
        }
        return next;
    }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值