KMP算法

KMP算法主要用于高效地进行字符串匹配,通过构建部分匹配表(next数组)来避免不必要的字符比较。next数组的值表示模式字符串中前缀和后缀的最大公共长度,从而在失配时快速调整搜索位置。Java实现中,`next()`函数计算这部分匹配表,`kmp()`函数则利用next数组进行字符串匹配。当找到匹配时,返回目标字符串在主字符串中的起始位置,否则返回-1。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

KMP算法用途

KMP算法主要的用途就是字符串匹配,例如查找字符串"ab"(目标字符串)在字符串"abc"(待查找字符串)中出现的位置。也就是查找字符串"abc"中是否包含字符串"ab",如果包含,返回包含的起始位置。

先看下Java实现
public int[] next(String str){
        int[] next = new int[str.length()];
        int j = 0;
        int k =-1;
        next[j] = k;
        while (j < next.length - 1){
            if(k == -1 || str.charAt(j) == str.charAt(k)){
                next[++j] = ++k;
            }else {
                k = next[k];
            }
        }
        return next;
    }

    public int kmp(String str1, String str2){
        int[] next = next(str2);
        int i = 0;
        int j = 0;
        while(i < str1.length()){
            if(j == -1 || str1.charAt(i) == str2.charAt(j)){
                i++;
                j++;
            }else {
                j = next[j];
            }
            if(j == str2.length()){
                return i - j;
            }
        }
        return -1;
    }
其中最重要的是next()函数

KMP算法的核心,是一个被称为部分匹配表PMT(Partial Match Table)的数组,也就是next()函数的返回值。
那什么是PMT呢?例如:对于字符串“abababca”,它的PMT如下表所示:
在这里插入图片描述
待匹配的模式字符串有8个字符,那么PMT就会有8个值。
解释PMT之前,先了解一下字符串的前缀和后缀。如果字符串A和B,存在A=BS,其中S是任意的非空字符串,那就称B为A的前缀。例如,”Harry”的前缀包括{”H”, ”Ha”, ”Har”, ”Harr”},我们把所有前缀组成的集合,称为字符串的前缀集合。同样可以定义后缀A=SB, 其中S是任意的非空字符串,那就称B为A的后缀,例如,”Potter”的后缀包括{”otter”, ”tter”, ”ter”, ”er”, ”r”},然后把所有后缀组成的集合,称为字符串的后缀集合。要注意的是,字符串本身并不是自己的后缀。
链接:https://www.zhihu.com/question/21923021/answer/281346746
来源:知乎
PMT中的值就是字符串的前缀集合与后缀集合的交集中最长元素的长度。例如,对于”aba”,它的前缀集合为{”a”, ”ab”},后缀 集合为{”ba”, ”a”}。两个集合的交集为{”a”},那么长度最长的元素就是字符串”a”了,长 度为1,所以对于”aba”而言,它在PMT表中对应的值就是1。

next()函数实现我们等会解释,先看看如何使用这个PMT表加速查找

如图 1.12 所示,要在主字符串"ababababca"中查找模式字符串"abababca"。如果在 j 处字符不匹配,那么模式字符串(0, j-1) 的位置是和主串(i-j, i-1)的位置是匹配的。由前边所说的模式字符串 PMT 的性质可知,模式字符串从 0 到 j−1 ,在这个例子中就是”ababab”,其前缀集合与后缀集合的交集的最长元素为”abab”, 长度为4,所以就可以断言,主字符串中i指针之前的 4 位一定与模式字符串的第0位至第 4 位是相同的,即长度为 4 的后缀与前缀相同。这样一来,我们就可以将这些字符段的比较省略掉。具体的做法是,保持i指针不动,然后将j指针指向模式字符串的PMT[j −1]位即可。
在这里插入图片描述

依据上面的思路,KMP算法如下

我们看到如果是在 j 位 失配,那么影响 j 指针回溯的位置的其实是第 j −1 位的 PMT 值,所以为了编程的方便, 我们不直接使用PMT数组,而是将PMT数组向后偏移一位。我们把新得到的这个数组称为next数组。我们令next[0] = -1;只是为了编程方便。

public int kmp(String str1, String str2){
        int[] next = next(str2);
        int i = 0;
        int j = 0;
        while(i < str1.length()){
            if(j == -1 || str1.charAt(i) == str2.charAt(j)){
                i++;
                j++;
            }else {
                j = next[j];
            }
            if(j == str2.length()){
                return i - j;
            }
        }
        return -1;
    }
next()实现原理解释

求next数组的过程完全可以看成字符串匹配的过程,即以模式字符串为主字符串,以模式字符串的前缀为目标字符串,一旦字符串匹配成功,那么当前的next值就是匹配成功的字符串的长度。
具体来说,就是从模式字符串的第一位(注意,不包括第0位)开始对自身进行匹配运算。 在任一位置,能匹配的最长长度就是当前位置的next值。如下图所示。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值