首页 > 编程语言 > 详细

KMP 算法

时间:2020-08-29 21:18:17      阅读:82      评论:0      收藏:0      [点我收藏+]

KMP 算法

假设我们在字符串 s 中查找是否有字符串 p(长度分别为 n, m),设整数 i, j 分别为字符串 s, p 中的游标。我们会发现暴力法每次匹配失败,i, j 都会回退,其中 i 的步进为 1j 则每次都回到 0。KMP 算法的主要思想就是让 i 不回退,只有 j 往回移动,因此只要能限制 j 移动的次数就可以确保时间复杂度够低。要保证 i 不回退,我们需要知道 j 该回退的步长,这就要求我们有额外的信息来源。

假设我们有一个长度为 n 的整型数组 next,其中 next[i] 表示 s[0] ~ s[i] 的字符串中相同的前缀以及后缀的最大长度。以 s = "ababcabcd" 为例:

i 前缀 后缀 最大长度
0 0
1 "a" "b" 0
2 "a", "ab" "a", "ba" 1
3 "a", "ab", "aba" "b", "ab", "bab" 2
4 "a", "ab", "aba", "abab" "c", "ba", "abc", "babc" 0
5 "a", "ab", "aba", "abab", "ababc" "a", "ca", "bca", "abca", "babca" 1
6 ... ... 2
7 ... ... 0
8 ... ... 0

这样,假如字符串 p = "ababa",那么在第一次匹配时,在 i = j = 4 时会失败,但这时 i 就不用回到 1 再开始匹配,因为 next 数组已经告诉它中间有一段你匹配不上,直接跳到可以匹配的部分就好,二者匹配的部分就是前后缀重复的部分(s 前后缀相同 + p 前缀和 s 前缀相同 = p 前缀和 s 后缀相同),所以 i 不用回到 1,待在原地就好,而 j 也不用回到 0,而是回到 2p 前缀 "ab"s 后缀 "ab" 相同)。

有了 next 数组我们可以发现,i, j 共有三种情况:

  1. i, j 同时自增 1(字符匹配成功)
  2. i 不变,j 回退( p 非首字符匹配失败)
  3. i 自增 1j 不变( p 首字符匹配失败)

简单分析我们可以发现,j 最多自增 min(n, m) 次,所以 j 最多回退 min(n, m) 次,也就是说 j 最多变化 2*min(n, m) 次,时间复杂度是线性的。

生成 next 我们使用和上方类似的思想,将它自己和自己匹配就可以找出相同的前后缀。依旧使用 i, j 作为游标,i 自增时更新 next,而 j 回退时用到的 next 在之前已经逐步计算出来了(动态规划),所以时间复杂度和上面的算法相仿(代码写出来都几乎一样),为 O(n),那么整体的时间复杂度就是 O(n+m)

Java 代码

public class KMP{
    public void search(String s, String p) {
        int[] next = this.getNext(s);
        int i = 0, j = 0, len = s.length();
        while (i < len) {
            if (s.charAt() == p.charAt(j)) {
                ++i; ++j;// 不能像 C 一样写成 ++i, ++j; 就要多个括号,不开心
            } else if (j != 0)
                j = next[j - 1]
            else
                ++i;
        }
    }

    public int[] getNext(String s) {
        int len = s.length();
        int[] next = new int[len];
        next[0] = 0;
        int i = 1, j = 0;
        while (i < len) {
            if (s.charAt(i) == s.charAt(j))
                // 对比 search,多了给 next 赋值
                next[i++] = ++j;
            else if (j != 0)
                // 对比 search 没变化
                j = next[j - 1];
            else
                // 对比 search,多了给 next 赋值
                next[i++] = 0;
           	if (j == len)
                System.out.println(i - j + 1);
        }
        return next;
    }
}

KMP 算法

原文:https://www.cnblogs.com/wilfredshen/p/13583335.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!