核心概述

KMP(Knuth–Morris–Pratt)算法用于在文本串中高效查找单一模式串的全部出现位置,通过预处理失配函数避免文本指针回溯,时间复杂度 O(n+m)。

原文引述

Description: Find all occurrences of pattern p in text s in O(|s|+|p|) using prefix function (failure function). No backtracking on the text; useful for single pattern matching. Time: O(|s|+|p|) Status: tested ——摘自本节点现有英文注释

展开阐述

  • 定义与背景

    • 目标:在文本 s 中寻找模式 p 的所有匹配起点。
    • 思想:利用模式自身的最长真前后缀信息(next/pi 数组)在失配时跳转,避免回退文本索引。
  • 接口/字段/数据结构与输入输出语义(按仓库约定)

    • 典型接口(据约定风格):
      • vector prefixFunction(const string& p)
        • 返回模式串 p 的 next(或 pi)数组。
      • vector kmpSearch(const string& s, const string& p)
        • 返回所有起始位置 i(0-index)使得 s[i..i+|p|−1] == p。
      • 可选:int kmpCount(const string& s, const string& p) 统计出现次数。
    • 输出语义:
      • kmpSearch 返回升序的起始位置列表;无匹配返回空。
  • 核心流程与关键要点

    • 预处理模式串 p 计算 next 数组:
      • next[0]=0;
      • 对 i=1..|p|−1:
        • j = next[i−1];
        • while (j>0 && p[i]!=p[j]) j = next[j−1];
        • 若 p[i]==p[j] 则 j++;
        • next[i]=j。
      • 语义:next[i] 为 p[0..i] 的最长真前缀且同时为后缀的长度。
    • 匹配阶段(扫描文本 s):
      • 维护当前已匹配长度 j;
      • 对每个字符 s[i]:
        • while (j>0 && s[i]!=p[j]) j = next[j−1];
        • 若 s[i]==p[j] 则 j++;
        • 若 j==|p|:
          • 记录匹配位置 i−|p|+1;
          • j = next[j−1](继续寻找重叠匹配)。
    • 正确性要点与不变式:
      • 不回退文本指针;不变式为“已匹配前缀长度 j 的后缀等于 p 的前缀”。
    • 数值/实现注意:
      • 字符比较为等值关系;支持一般字符集。
      • 返回索引约定为 0-index,注意与题面一致。
  • 复杂度与边界条件

    • 复杂度:预处理 O(|p|),匹配 O(|s|),总 O(|s|+|p|);空间 O(|p|)。
    • 边界:空模式(通常定义为匹配所有位置或直接返回空,依实现约定);空文本返回空。
    • 常见变体/扩展:
      • 循环串匹配可在 p+p 中查找 s(注意长度截断)。
      • 与 Z 函数等价用于单模式匹配(见 126-字符串-Z函数)。
  • 与相邻技术的取舍对比

关联节点