KMP算法之我见

本文详细介绍了KMP算法的原理及实现过程,包括如何构建next数组来记录模式串的最长相同前后缀长度,以及如何利用next数组高效地进行字符串匹配。

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

KMP算法(时间复杂度O(m + n))最核心的是求next数组,然后再根据next数组来进行模式匹配。因此分为两部分:

1.求next数组

next数组记录的是模式串的特征,即最长相同前后缀
考虑如下模式串P:ABCDABC
它的前缀有:A,AB,ABC,ABCD,ABCDA,ABCDAB
它的后缀有:C,BC,ABC,DABC,CDABC,BCDABC
因此它的最长相同前后缀是ABC,长度是3.
next数组中p[i]中存的就是模式串p[0]…p[i]的最长相同前后缀长度
因此它的next数组就是如下形式:

Next数组:

模式串PABCDABC
next数组0000123

2.用求出的next数组进行字符串匹配

有了next数组后,我们就可以进行匹配了,考虑如下匹配串:

ABCABCDABDDABCDABC
ABCDABC
  ↑

当匹配到D时,发现不匹配了,普通的做法会把模式串p后移一位继续匹配,像这样:

ABCABCDABDDABCDABC
  ABCDABC
  ↑

但我们发现,根据next数组,k-1位即第一个c对应的值为0,即前面的ABC在模式串后移的过程中不能再出现匹配的过程了,因此可以直接中模式串的第0位开始匹配,像这样:

ABCABCDABDDABCDABC
       ABCDABC
       ↑

我们接着继续匹配:

ABCABCDABDDABCDABC
       ABCDABC
       ↑

发现D和C不同,再根据next数组k-1位即第二个B的值2,我们可以接下从第二位(下标从0开始)开始匹配,像这样:

ABCABCDABDDABCDABC
       ABCDABC
       ↑

继续按上述方法匹配直到找到匹配的串为止,找不到返回-1

ABCABCDABDDABCDABC
         ABCDABC
              ↑

下面是对应的代码:
public class Solution {
    public static void next(String p, int[] next) {
        int pl = p.length();
        for (int i = 1, k = 0; i < pl; i++) {// k表示p[0]…P[i - 1]的最长相同前后缀
            while (k > 0 && p.charAt(i) != p.charAt(k)) {
                k = next[k - 1];
            }
            if (p.charAt(i) == p.charAt(k)) {
                k++;
            }
            next[i] = k;
        }
    }

    public static int kmp(String t, String p) {
        int tl = t.length();
        int pl = p.length();
        int[] next = new int[pl];
        next(p, next);

        for (int i = 0, k = 0; i < tl; i++) {
            while (k > 0 && t.charAt(i) != p.charAt(k)) {
                k = next[k - 1];
            }
            if (t.charAt(i) == p.charAt(k)) {
                k++;
            }
            if (k == pl) {
                return i - k + 1;//输出匹配的第一个位置
            }
        }
        return -1;
    }

    public static void main(String[] args) {

        String T = "ABCABCBABCDABC";
        String P = "ABCDABC";
        System.out.println(kmp(T, P));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值