当前位置: 首页 > news >正文

高效字符串匹配:KMP算法

本文针对计算机统考408的数据结构复习需求,系统解析KMP算法的核心原理与实现要点。​ 

KMP算法的核心思想

当主串与模式串匹配失败时,利用已匹配部分的最长公共前后缀长度,确定模式串滑动距离,避免主串指针回溯。时间复杂度:​O(n+m)

方便理解我们先来看一个例子:

本次匹配的模式串为abaabc,主串在扫描前未知

匹配到下标3失败时,可以确定主串前三位为aba,第4为不是a 

很明显,我们将子串向右移一位是不可能匹配的, 这样一开始就匹配失败了,而我们向右移动两位,有可能可以匹配成功,因此我们令j=1继续匹配。由j=3匹配失败后令j=1,构建next数组(表示该处字符不匹配时应该回溯到的字符的下标),令next[3]=1。

构建next数组 (手算)

 假设匹配到第i位失败了,说明主串和模式串前i-1位是相同的,设next[i] = j (n>0)。

由左边的图可知:S[0]=T[0],S[1]=T[1],...,S[n-1]=T[i-1]

由右边的图可知:S[i-j]=T[0],...,S[i-1]=T[j-1]

联立两式,得:T[0]=T[i-j],T[1]=T[i-j+1],...,T[j-1]=T[i-1]

简单来说,next[j] = 前 j - 1 位的最长相等前后缀长度。

拿刚才模式串abaabc举例

下标i前i位子串后i位子串最长相等前后缀长度next[i]
0空(首字符失配)-1
100
2ab00
3a, aba, ba11
4a, ab, abaa, ba, baa11
5a, ab, aba, abaab, ab, aab, baab22

最终next数组中的元素应该是{-1, 0, 0, 1, 1, 2}。

理解上面的思路,接下来用代码来实现求模式串的next数组

匹配字符串

假设此时已经获得next数组,接下来进行匹配,不理解可以画个草图模拟一下代码。

// 字符串匹配,返回匹配的首字母位置
int kmpSearch(const string& S, const string& T, int next[]) {
    int i = 0, j = 0;                        // i为主串的指针,j为模式串指针
    int lenS = S.size(), lenT = T.size();
    while(i < lenS && j < lenT) {
        if(j == -1 || s[i] == p[j]) {        // 如果模式串无法右移或者单词匹配成功
            i++;                             // 两指针都往右移一位,继续匹配
            j++;
        }
        else j = next[j];                      // 匹配失败,回退j指针
    }
    if(j == lenT) return i - j;              // 模式串全部匹配成功,输出首字母位置
    return -1;
}

构建next数组 (机算)

本质上与匹配字符串的操作大差不差,核心跳转逻辑分三种情况:

1. j在初始位置-1 → 重置为0开始匹配
2. 字符匹配 → 同步前进
3. 字符不匹配 → 跳转到前一个匹配位置 

void get_next(const string& T, int next[]) {
    int i = 0, j = -1;
    next[0] = -1;
    while (i < n - 1) {
        if (j == -1 || p[i] == p[j]) { 
            j++;    
            i++;    
            next[i] = j;                 // 前后缀相等的最大值
        } else {
            j = next[j];                 // 回退寻找可能匹配的位置
        }
    }
}

结语

KMP算法通过预处理模式串的next数组,将匹配过程中的无效比较降至最低,是空间换时间的经典设计。备考时需重点掌握next数组的手算推导​(最长相等前后缀计算),可以结合408真题巩固练习。

相关文章:

  • vscode连接本地mysql数据库
  • AIGC智能体(Ai星图)
  • vue-cli如何正确关闭prefetchprefetch和preload
  • go命令使用
  • 【40】单片机编程核心技巧:static 的重要作用
  • Retrofit中scalars转换html为字符串
  • 在树莓派上如何使用 Vosk检测唤醒词
  • 【Java SE】final关键字
  • mayfly-go开源的一站式 Web 管理平台
  • K8S-etcd备份还原操作手册
  • 网络华为HCIA+HCIP NFV
  • 996引擎-接口测试:消息Tips
  • 【leetcode hot 100 17】电话号码的字母组合
  • GCC 预定义宏:解锁编译器的隐藏信息
  • 使用Docker运行 Ollama本地部署 DeepSeek 模型并用Dify实现可视化操作
  • 使用DeepSeek查找资料:C++ sprintf % 种类
  • 视频剪辑行业的现状与进阶之路:一个双视角分析
  • 《Android 13深度定制:手势拦截技术实现SystemUI状态栏智能折叠方案》
  • Java-校验值区间值的连续性
  • 每天学一个 Linux 命令(6):shutdown
  • 中华人民共和国和巴西联邦共和国关于强化携手构建更公正世界和更可持续星球的中巴命运共同体,共同维护多边主义的联合声明
  • 支持企业增强战略敏捷更好发展,上海市领导密集走访外贸外资企业
  • 福州千余公共道路泊车位装“智能地锁”续:运营公司被责令改正并罚款
  • 中国恒大:清盘人向香港高等法院申请撤回股份转让
  • 马上评丨75万采购300元设备,仅仅终止采购还不够
  • 商务部就开展打击战略矿产走私出口专项行动应询答记者问