字符串算法完全指南:从 KMP 到 Manacher 与后缀数组

字符串算法是程序员经常碰到的领域 —— 文本搜索、模糊匹配、拼写检查、DNA 序列分析。但很多人对它的认识停留在 indexOf。真正的字符串算法精彩得多:KMP 让你跳过重复匹配,Manacher 在 O(n) 求最长回文,后缀数组让任意子串查询 O(log n)。这篇文章把核心字符串算法讲透。

朴素字符串匹配

找 pattern 在 text 中的位置。最朴素:逐位置尝试。

function naiveSearch(text, pattern) {
    for (let i = 0; i <= text.length - pattern.length; i++) {
        let match = true;
        for (let j = 0; j < pattern.length; j++) {
            if (text[i + j] !== pattern[j]) { match = false; break; }
        }
        if (match) return i;
    }
    return -1;
}
// 复杂度 O(n*m),最坏情况 text = "aaaa...aab",pattern = "aab"

KMP 算法

核心思想:匹配失败时,利用已匹配的信息跳过一些位置,不必回到 text 开始。

// 构建 next 数组:next[i] 表示 pattern[0..i] 的最长真前缀和真后缀的长度
function buildNext(pattern) {
    const next = new Array(pattern.length).fill(0);
    let j = 0;
    for (let i = 1; i < pattern.length; i++) {
        while (j > 0 && pattern[i] !== pattern[j]) j = next[j - 1];
        if (pattern[i] === pattern[j]) j++;
        next[i] = j;
    }
    return next;
}

function kmp(text, pattern) {
    const next = buildNext(pattern);
    let j = 0;
    for (let i = 0; i < text.length; i++) {
        while (j > 0 && text[i] !== pattern[j]) j = next[j - 1];
        if (text[i] === pattern[j]) j++;
        if (j === pattern.length) {
            return i - j + 1;   // 找到匹配位置
        }
    }
    return -1;
}

KMP 把复杂度从 O(n*m) 降到 O(n+m)。grep 早期实现就用 KMP。

Boyer-Moore

从 pattern 的末尾往前比,匹配失败时根据"坏字符"和"好后缀"规则跳过更多位置。实际比 KMP 更快(每步可能跳过 m 个字符,而不是 1 个)。

grep、Java String.indexOf 等成熟实现用 Boyer-Moore-Horspool(简化版)。

Rabin-Karp:哈希匹配

思路:计算 pattern 的哈希,然后在 text 上滚动哈希,哈希匹配时再实际比对。

function rabinKarp(text, pattern) {
    const m = pattern.length, n = text.length;
    const base = 31, mod = 1e9 + 7;

    let patternHash = 0, textHash = 0, power = 1;
    for (let i = 0; i < m; i++) {
        patternHash = (patternHash * base + pattern.charCodeAt(i)) % mod;
        textHash = (textHash * base + text.charCodeAt(i)) % mod;
        if (i < m - 1) power = (power * base) % mod;
    }

    for (let i = 0; i <= n - m; i++) {
        if (textHash === patternHash && text.slice(i, i + m) === pattern) {
            return i;
        }
        if (i < n - m) {
            textHash = ((textHash - text.charCodeAt(i) * power) * base + text.charCodeAt(i + m)) % mod;
            if (textHash < 0) textHash += mod;
        }
    }
    return -1;
}

Rabin-Karp 的优势:多模式匹配(同时找多个 pattern)。哈希集合一次匹配 N 个 pattern。

Aho-Corasick:多模式匹配

Trie + KMP 的组合。一次扫描 text 找出所有 pattern 的所有出现。复杂度 O(n + m + k),k 是匹配数。

应用:敏感词过滤、入侵检测、广告过滤。nginx、Snort 内部都用。

Manacher:最长回文子串

朴素 O(n²),Manacher 算法 O(n)。核心思想:利用回文的对称性,已知 a 是 b 的回文中心时,b 内部的回文长度可以推导。

function manacher(s) {
    // 预处理:在每个字符两边插 #,统一奇偶长度回文
    const t = '#' + s.split('').join('#') + '#';
    const p = new Array(t.length).fill(0);
    let center = 0, right = 0;
    let maxLen = 0, maxCenter = 0;

    for (let i = 0; i < t.length; i++) {
        const mirror = 2 * center - i;
        if (i < right) p[i] = Math.min(right - i, p[mirror]);

        while (i + p[i] + 1 < t.length &&
               i - p[i] - 1 >= 0 &&
               t[i + p[i] + 1] === t[i - p[i] - 1]) {
            p[i]++;
        }

        if (i + p[i] > right) { center = i; right = i + p[i]; }
        if (p[i] > maxLen) { maxLen = p[i]; maxCenter = i; }
    }

    const start = (maxCenter - maxLen) / 2;
    return s.substr(start, maxLen);
}

后缀数组(Suffix Array)

一个字符串所有后缀按字典序排序后的索引数组。应用:

  • 任意子串查询 O(m log n)。
  • 最长公共子串(多串)。
  • 生物信息学序列分析。
function suffixArray(s) {
    const n = s.length;
    const sa = Array.from({length: n}, (_, i) => i);
    sa.sort((a, b) => s.slice(a).localeCompare(s.slice(b)));
    return sa;
}

// O(n log² n) 的优化版本用倍增排序,O(n log n) 用 SA-IS 算法

后缀数组 + LCP(最长公共前缀)数组配合,几乎所有字符串问题都能解。

编辑距离(Levenshtein)

把字符串 A 改成 B 最少需要多少次操作(插/删/改)。经典动态规划。详见之前算法 DP 文章。

应用:拼写检查、DNA 比对、Diff 算法、模糊搜索。

Boyer-Moore vs KMP vs Rabin-Karp 选型

场景                    推荐
单 pattern 大文本搜索   Boyer-Moore(实际最快)
教学 / 不要回退         KMP(理论清晰)
多 pattern 同时搜       Aho-Corasick
任意子串频繁查询        后缀数组 / 后缀树
模糊匹配 / 相似度       编辑距离

正则表达式 vs 字符串算法

正则比专门算法更通用,但单一固定模式时慢。grep -F(fixed string)模式跳过正则引擎,用 Boyer-Moore,比正则快几倍。

大规模搜索的工程方案

  • Elasticsearch / Solr:倒排索引 + BM25 评分,适合"关键词搜索"。
  • Lucene:ES / Solr 底层。
  • ripgrep:命令行的 grep 替代,极致优化的字符串搜索。
  • Hyperscan:Intel 的高性能正则匹配库,网络入侵检测系统用。

写在最后

字符串算法看似"学术",但日常工程里到处用到 —— 你用 IDE 的全文搜索、用 grep、用日志分析、用浏览器 Find,背后都是这些算法。理解它们的复杂度和适用场景,能让你做出更好的工具选择,且面试时也能脱口而出。

一图看懂

KMP 状态机一图看懂:

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

图算法完全指南:从 BFS/DFS 到 Dijkstra、A*、拓扑排序

2026-5-15 17:48:03

技术教程

贪心、回溯、分治完全指南:算法三大范式与位运算技巧

2026-5-15 17:48:03

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索