算法 - KMP算法

子串的定位操作通常称为串的模式匹配,它求的是子串(常称为模式串)在主串中的位置。暴力匹配算法会产生 \(O(nm)\) 的时间复杂度,其中 \(n\) 是主串长度,\(m\) 是模式串长度。而 KMP 算法能将匹配的时间复杂度降低到线性时间 \(O(n+m)\),本文将介绍 KMP 算法的核心思想与代码示例。

1. KMP 算法思想

KMP 算法的关键是避免重复的比较。它通过一个预处理的步骤,构建一个部分匹配表(Partial Match Table),使用这个表来指定每次匹配后模式串的移动距离,以跳过一些主串中显然不匹配的位置。

1.1 字符串的前缀、后缀与部分匹配值
  • 前缀指除最后一个字符以外,字符串的所有头部子串;
  • 后缀指除最前一个字符以外,字符串的所有尾部子串;
  • 部分匹配值是每个位置前的最长相等前后缀长度。

以模式串 abcac 为例,它的部分匹配表为:

Index 1 2 3 4 5
Pattern a b c a c
Prefix \(\{\}\) \(\{a\}\) \(\{a, ab\}\) \(\{a, ab, abc\}\) \(\{a, ab, abc, abca\}\)
Suffix \(\{\}\) \(\{b\}\) \(\{c, bc\}\) \(\{a, ca, bca\}\) \(\{c, ac, cac, bcac\}\)
PM 0 0 0 1 0
1.2 匹配过程

设主串为 ababcabcacbab,模式串为 abcac,以下是字符串的匹配过程。设 \(i\) 为主串匹配位置的下标,\(j\) 为模式串匹配位置的下标,则移动位数 \(d\) 的计算公式为:

\[d = (j-1) - PM(j-1) \]

第一趟比较过程:

s a b a b c a b c a c b a b
p a b c

比较发现 \(j = 2\) 位置的字符 a 与 c 不匹配,查表可知最后一个匹配字符 b 的部分匹配值为 0,则移动位数为 \(d = 2 - 0 = 2\),将模式串向后移动两位。

第二趟比较过程:

s a b a b c a b c a c b a b
p a b c a c

比较发现 \(j=4\) 位置的字符 b 与 c 不匹配,计算移动位置 \(d = 4 - 1 = 3\)

第三趟比较过程:

s a b a b c a b c a c b a b
p a b c a c

模式串全部比较完成,匹配成功。

2. KMP 算法实现

2.1 构建部分匹配表
std::vector<int> buildPMT(std::string& pattern) {
    int m = pattern.size();
    std::vector<int> pmt(m, 0);
    
    int len = 0;
    for (int i = 1; i < m; i++) {
       	while (len > 0 && pattern[i] != pattern[len]) {
            len = pmt[len - 1];
        }
        if (pattern[i] == pattern[len])
            len++;
        pmt[i] = len;
    }
    
    return pmt;
}
2.2 比较
std::vector<int> KMPSearch(std::string& text, std::string& pattern) {
    int n = text.size();
    int m = pattern.size();
    std::vector<int> pmt = buildPMT(pattern);
    std::vector<int> match;
    
    int i = 0;
    int j = 0;
    
    while (i < n) {
        if (text[i] == pattern[j]) {
            i++;
            j++;
        }
        if (j == m) {
            match.push_back(i - j);
            j = pmt[j - 1];
        } else if (i < n && text[i] != pattern[j]) {
            if (j > 0)
                j = pmt[j - 1];
            else
                i++;
        }
    }
    
    return match;
}
posted @ 2025-04-14 10:55  木杉的园子  阅读(59)  评论(0)    收藏  举报