一天一个经典算法:KMP算法(春节档)

306 阅读2分钟

「这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战」。

KMP算法全称Knuth-Morris-Pratt字符串查找算法,可在一个字符串S内查找一个词W的出现位置。一个词在不匹配时,本身就包含足够的信息来确定下一个匹配可能的开始位置,此算法利用这一特性以避免重新检查先前配对的字符。

这个算法由高德纳和沃恩·普拉特在1974年构思,同年詹姆斯·H·莫里斯也独立地设计出该算法,最终三人于1977年联合发表。

KMP 算法巧妙地使用了之前的比较数据。它可以在O(n)时间内搜索模式,因为它永远不会重新比较与模式符号匹配的文本符号。但是,它使用部分匹配表来分析模式结构。部分匹配表的构建需要O(m)时间。因此,KMP 算法的整体时间复杂度为O(m + n)。

案例

源文本text:ABCABAABCABAC,需要匹配的字符串pattern:CAB; 查找是需要同时使用两个循环变量ij

i:代表源文本字符串text内匹配字符串pattern的当前查找位置。

j:代表匹配字符串pattern当前做比较的字符位置。

图示:

image.png

Java代码:

class Main
{
    // 实现 KMP 算法
    public static void KMP(String text, String pattern)
    {
        // 匹配文本为null
        if (pattern == null || pattern.length() == 0)
        {
            System.out.println("The pattern occurs with shift 0");
            return;
        }
 
        // 文本为 NULL,或者源文本的长度小于匹配文本的长度
        if (text == null || pattern.length() > text.length())
        {
            System.out.println("Pattern not found");
            return;
        }
 
        char[] chars = pattern.toCharArray();
 
        // next[i] 存储下一个最佳部分匹配的索引
        int[] next = new int[pattern.length() + 1];
        for (int i = 1; i < pattern.length(); i++)
        {
            int j = next[i + 1];
 
            while (j > 0 && chars[j] != chars[i]) {
                j = next[j];
            }
 
            if (j > 0 || chars[j] == chars[i]) {
                next[i + 1] = j + 1;
            }
        }
 
        for (int i = 0, j = 0; i < text.length(); i++)
        {
            if (j < pattern.length() && text.charAt(i) == pattern.charAt(j))
            {
                if (++j == pattern.length()) {
                    System.out.println("在索引" + (i - j + 1) +"找到");
                }
            }
            else if (j > 0)
            {
                j = next[j];
                i--;    // i将在下一次循环中递增
            }
        }
    }
 
    // 用Java实现KMP算法的程序
    public static void main(String[] args)
    {
        String text = "ABCABAABCABAC";
        String pattern = "CAB";
 
        KMP(text, pattern);
    }
}

输出:在索引2找到,在索引8找到。

KMP优缺点

KMP最大的优势就是可以保证最坏情况下的效率。预处理时间总是O(n),搜索时间总是O(m)。缺点是在字符串重叠部分不多的情况下,KMP可能并不是那么必要。

参考:cs.indstate.edu/~kmandumula…