KMP字符串匹配算法

引言

KMP算法的思想在于,让每一次匹配都尽可能使用先前匹配产生的信息(部分匹配表)。

KMP算法的难点在于,部分匹配表的构造也在尽可能使用之前构造过程中的信息(动态规划?)。

暴力 Vs KMP

暴力:失配,字串向后1位,成功匹配字符数归0。

KMP:失配,字串向后n位,成功匹配数m位。n,m的计算即KMP算法的核心。

找规律

??          ??
abcdabcy     abcdabcy

a?          a?
abcdabcy     abcdabcy

ab?         ab?
abcdabcy      abcdabcy

abc?        abc?
abcdabcy       abcdabcy

abcd?       abcd?
abcdabcy        abcdabcy

abcda       abcda?
abcdabcy        abcdabcy

abcdab?     abcdab?
abcdabcy        abcdabcy

abcdabc?    abcdabc?
abcdabcy        abcdabcy

abcdabcy
abcdabcy

观察得知,移动的位数分别需要考察 a ab abc abcd abcda abcdab abcdabc的前缀和后缀是否一样。
same[index]可以理解为pattern[index]之前的前后缀匹配最大长度
为了使得move[index] = index - same[index],规定same[0] = -1

index01234567
pattern[index]abcdabcy
same[index]-10000123
move[index]11234444

部分匹配表(失配函数)

上一部分中最后的表格能极大的帮助我们完成字符串的匹配,它也就是所谓的部分匹配表。使用该表的时机即pattern[index]无法匹配,所以该表也称失配函数

(难点)部分匹配表的构造

以 aabaabaaa为例子,构造部分匹配表

index012345678
pattern[index]aabaabaaa
same[index]-101012345
move[index]111333333

类似动态规划问题,如果我们已知same[index-1] = k,那么pattern[0...(index-2)]前后缀匹配最大长度已知,即pattern[0...k-1] == pattern[(index-k-1)...(index-2)]
在求same[index]时我们需要考虑新的字符pattern[index - 1],也就增加了1个后缀需要考虑的字符。

  • 如果pattern[k] == pattern[index-1],那么pattern[0...k] == pattern[(index-k-1)...(index-1)],那么same[index] = same[index - 1] + 1
  • 如果pattern[k] != pattern[index-1],则需要回溯到更小的匹配长度,一定小于k。我们考虑same[k]的定义,即pattern[k]之前的前后缀最大匹配长度,是能回溯到的最好的匹配长度。
    • 故继续判断pattern[same[k]]pattern[index - 1]的大小关系,无法得出结果则继续回溯。
    • 如果回溯到初始情况因为same[0] == -1,则same[index] = 0

java代码

public static int[] getSame(String modelString) {
    int[] same = new int[100];
    same[0] = -1;
    int i = 0;
    int j = same[i];
    while (i < modelString.length()) {
        if (j == -1 || modelString.charAt(i) == modelString.charAt(j)) {
            // same[i+1] = same[i] + 1
            i++;
            j++;
            same[i] = j;
        } else {
            // backtrack
            j = same[j];
        }
    }
    return same;
}

最后

same可能就是其他文章中提到的next数组

参考

KMP字符串匹配算法【双语字幕】

wiki百科

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值