logo

关于 KMP 算法

May 5, 2022 · 3min

    首先需要明白 KMP 算法是什么,可以阅读这两篇博客文章:KMP 算法 - 刘毅字符串匹配的KMP算法 - 阮一峰

    实现 KMP 算法的关键是 双指针next 数组的生成。next[j] 表示当 j 位置的字符不匹配时,j 应该移动到哪个位置。next[j] 是 -1 时表示要完全跳过(i++;j = 0;),比如 next[0] 一定是 -1,因为第一个字符都不匹配,当然就是直接跳过了。

    这里我讲一下 KMP 算法的 next 数组的生成。next 数组是通过模式串自己跟自己匹配计算出来的,假设我们的模式串,即搜索的目标是“abac”,其 next 数组的计算过程如下:

    // (1) 一开始,next[0] == -1
                  i = 1
    next =  -1
           ['a', 'b', 'a', 'c']
                ['a', 'b', 'a', 'c']
                  j = 0
    
    
    // (2.0) 当 next[i] != next[j] 时,next[i] = j
                  i = 1
    next =  -1    0
           ['a', 'b', 'a', 'c']
                ['a', 'b', 'a', 'c']
                  j = 0
    
    // (2.1) j = next[j]
                  i = 1
    next =  -1    0
           ['a', 'b', 'a', 'c']
                ['a', 'b', 'a', 'c']
             j = -1
    
    // (3) i++; j++;
                       i = 2
    next =  -1    0
           ['a', 'b', 'a', 'c']
                     ['a', 'b', 'a', 'c']
                       j = 0
    
    // (4) 当 next[i] == next[j] 时,next[i] = next[j]
                       i = 2
    next =  -1    0   -1
           ['a', 'b', 'a', 'c']
                     ['a', 'b', 'a', 'c']
                       j = 0
    
    // (5) i++; j++;
                            i = 3
    next =  -1    0   -1
           ['a', 'b', 'a', 'c']
                     ['a', 'b', 'a', 'c']
                            j = 1
    
    // 剩下就是根据 next[i] 和 next[j] 是否相等,循环 (2.0) 或 (4) 操作
    

    KMP 算法的时间复杂度为 O(n + m) ,空间复杂度为 O(m) ,它的 Java 的实现如下:

    public final class KMP {
    
        private KMP() {
            // private
        }
    
        public static int search(String str, String target) {
            int n = str.length(), m = target.length();
            if (m == 0) return 0;// 这一行不能少
            if (n < m) return -1;
            int[] next = getNext(target);
            for (int i = 0, j = 0; i < n; i++, j++) {
                while (j > -1 && str.charAt(i) != target.charAt(j)) j = next[j];
                if (j == m - 1) return i - j;
            }
            return -1;
        }
    
        static int[] getNext(String target) {
            int[] next = new int[target.length()];
            next[0] = -1;
            for (int i = 1, j = 0; i < target.length(); i++, j++) {
                if (j == -1) continue;
                if (target.charAt(i) != target.charAt(j)) {
                    next[i] = j;
                    j = next[j];
                } else {
                    next[i] = next[j];
                }
            }
            return next;
        }
    }
    

    由于

    • 空间复杂度 O(m) 不够好
    • 实现过程复杂,容易出错

    实际应用中很少选择 KMP 算法。

    CC BY-NC-SA 4.0 2021-PRESENT © Edsuns