KMP算法案例详解及实例

KMP算法是一种高效的字符串匹配算法,由Knuth、Morris和Pratt提出。它通过next数组避免主串指针回溯,提高了匹配效率。next数组的计算基于模式串的最大公共前后缀,与主串无关。本文介绍了KMP算法的概念、next数组的计算方法,并给出了代码实例。

一、什么是KMP算法?

KMP 算法是 D.E.Knuth、J,H,Morris 和 V.R.Pratt 三位大佬共同提出的一种字符串模式匹配算法,所以大家就称之为 Knuth-Morria-Pratt 算法,简称为KMP 算法。KMP算法相对于 Brute-Force(暴力)算法有比较大的改进,主要是消除了主串指针的回溯,通过next数组来帮助我们在不进行主指针回溯的情况下,对模式串进行匹配,从而使算法效率有了某种程度的提高。

二、名词释义

主串:匹配的主体
模式串:在主串字符串中需要匹配的目标字符串
next数组:用于帮助模式串在主串中计算偏移量的数组

三、KMP算法运行逻辑

都说kmp可以避免主指针回溯,从而达到效率提升的目的,其底层最核心的,就是next数组的计算。
在kmp算法里,next数组又代表着什么呢?在简介里咱们说next数组存的是模式串的偏移量,但这个偏移量又该怎么计算呢?首先我们来看一个数组:

 0 1 2 3 4 5 6 7 8 9
 a a b a b a a b a b

假设上边的数组就是需要匹配的模式串pattern,我们的模式串可能在任何一个位置发生不匹配,所以计算next数组,就是计算每个位置发生不匹配时的偏移量,但偏移量又怎样计算?这就要引入另一个匹配算法中的概念了:最大公共前后缀
假设我们在字符串的第一位第二位就发生了不匹配的状况,不用想,只需要将主串的匹配下标往后移动一位然后继续与当前模式串首位重新匹配,但在上边的数组中,我们如果已经匹配到了8号位,即aababaab都完全相同,唯独8号下标的a发生了不匹配的情况,这时难道我们继续将指针回溯到模式串首位并+1后重新匹配吗?那就是传统的暴力算法了,kmp算法的提出者们发现,已经完全匹配的aababaab中,出现了公共的前后缀aab,那为什么不能直接将模式串的匹配下标移动到该位置也就是从模式串的3号位重新比较,因为公共前后缀的关系,我们避免了aab三个字符的重复比较,进而提升了效率,所以!我们next数组的计算思路就有了,计算不匹配数组下标前的字串中的最大公共前后缀
了解了next数组的计算思路,我们可以发现,其实它的偏移量计算与主串没有任何关系,只与模式串本身有关,那我们仍然以上边的字符串数组来演示计算他的next数组
因为公共前后缀的原因,我们可以直接将数组首位置为-1,第二位置为0即

next[0] = -1;
next[1] = 0;

从第三位数开始,我们就要开始计算最大公共前后缀了,若第三位不匹配,其前缀aa的最大公共前后缀为a,即长度为1

next[2] = 1;

表明若此处发生了不匹配,主串的字符数组下标不变,但下一次匹配从模式串的1号下标位进行匹配,一次类推,我们可以计算出模式串中每个位置的公共前后缀,具体对应关系如下next数组示例:

 0 1 2 3 4 5 6 7 8 9
 a a b a b a a b a b
-1 0 1 0 1 0 1 2 3 4

四、代码实例

当我们计算出next数组,KMP的简单使用就已经成功一大半了,接下来就是对next数组的使用,直接上代码!

package hard;

/**
 * @author wangyifan
 * @create 2021/3/6 11:47
 */
public class KMP匹配 {
    public static void main(String[] args) {
        String str = "aabaaababaababba";

        System.out.println(match(str, "aababaabab"));

    }


    //获取next数组

    public static int[] getNextArray(char[] t) {
        int[] next = new int[t.length];
        if (next.length > 1) {
            next[0] = -1;
            next[1] = 0;
            int k;
            for (int j = 2; j < t.length; j++) {
                k=next[j-1];
                while (k!=-1) {
                    if (t[j - 1] == t[k]) {
                        next[j] = k + 1;
                        break;
                    }
                    else {
                        k = next[k];
                    }
                    next[j] = 0;  //当k==-1而跳出循环时,next[j] = 0,否则next[j]会在break之前被赋值
                }
            }
        }else if (next.length == 1){
            next[0] = 0;
        }
        return next;
    }


    public static int match(String str,String pattern){
        if (str.isEmpty()){
            return -1;
        }
        if (pattern.isEmpty()){
            return 0;
        }

        //获取字符数组
        char[] strs = str.toCharArray();
        char[] pats = pattern.toCharArray();
        //根据模式串获取next数组表
        int[] next = getNextArray(pats);

        int i = 0 , j = 0;
        while(i < strs.length && j < pats.length){
            //如果j = -1,说明第一个字符就不匹配,需要后移一位进行比对
            if (j == -1 || strs[i] == pats[j]){
                i++;
                j++;
            }else {
                j = next[j];
            }
        }
        if (j == pats.length){
            return i -j;
        }
        return -1;
    }
}

以上就是kmp算法的介绍简单使用~

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值