C++KMP算法

KMP 算法详解:从暴力匹配到高效字符串查找(附 C++ 代码)

作者:poplar
标签:C++ / 字符串 / KMP / 算法 / 面试

在 LeetCode 第 28 题 「实现 strStr()」 中,要求我们在一个字符串(haystack)中查找另一个字符串(needle)首次出现的位置。
如果用暴力法,时间复杂度是 O(n×m),但在实际工程和面试中,我们更希望用 O(n + m) 的高效算法 —— 这就是 KMP(Knuth-Morris-Pratt)算法

本文将带你从零彻底搞懂 KMP,包括:

  • 为什么需要 KMP?
  • 什么是前缀表(Prefix Table)?
  • 如何构建 next 数组?
  • 如何用 next 数组进行匹配?
  • 两种主流实现方式(前缀表减一 vs 不减一)

一、为什么需要 KMP?—— 暴力匹配的痛点

假设:

  • haystack = "aabaabaafa"
  • needle = "aabaaf"

暴力匹配过程如下:

  1. haystack[0] 开始匹配,前 5 个字符都匹配成功(a a b a a
  2. 到第 6 个字符时,haystack[5] = 'b'needle[5] = 'f'不匹配
  3. 暴力法会回退到 haystack[1] 重新开始匹配

但注意:前面已经匹配了 “aabaa”,其中包含重复信息(如前缀 “aa” 和后缀 “aa” 相同)。
KMP 的核心思想就是:利用已匹配部分的信息,避免从头开始匹配!


二、KMP 的灵魂:前缀表(Prefix Table)

✅ 什么是前缀?什么是后缀?

对字符串 "aabaaf"

  • 前缀:不包含最后一个字符的所有以第一个字符开头的连续子串
    "a", "aa", "aab", "aaba", "aabaa"
  • 后缀:不包含第一个字符的所有以最后一个字符结尾的连续子串
    "f", "af", "aaf", "baaf", "abaaf"
⚠️ 注意:前缀 ≠ 子串,必须从头开始;后缀必须到尾结束。

✅ 什么是最长相等前后缀?

对每个位置 i,计算 s[0..i]最长相等前后缀长度

例如:

子串最长相等前后缀长度
"a"0
"aa""a" == "a"1
"aab"0
"aaba""a" == "a"1
"aabaa""aa" == "aa"2
"aabaaf"0

所以前缀表为:[0, 1, 0, 1, 2, 0]

🎯 前缀表的作用:当匹配失败时,告诉我们模式串应该跳到哪个位置继续匹配

三、next 数组:前缀表的两种实现方式

在 KMP 中,通常用 next 数组来存储前缀表。但有两种主流实现:

方式 1️⃣:前缀表不减一(直接使用)

  • next[i] = s[0..i] 的最长相等前后缀长度
  • 示例:"aabaaf"next = [0, 1, 0, 1, 2, 0]

方式 2️⃣:前缀表统一减一(右移一位)

  • next[0] = -1,其余 next[i] = 原前缀表[i-1]
  • 示例:"aabaaf"next = [-1, 0, -1, 0, 1, -1]
💡 两种方式都能正确工作,只是匹配时的指针逻辑不同
本文将分别讲解两种实现!

四、方式一:前缀表不减一(推荐初学者)

步骤 1:构建 next 数组

voidgetNext(int* next,const string& s){ int j =0;// j 表示前缀末尾 next[0]=0;for(int i =1; i < s.size(); i++){ // 前后缀不相同,j 回退while(j >0&& s[i]!= s[j]){  j = next[j -1];}// 前后缀相同,j 向前移动if(s[i]== s[j]){  j++;} next[i]= j;// 记录当前最长相等前后缀长度}}

步骤 2:使用 next 数组匹配

intstrStr(string haystack

Read more

【Linux】Nginx配置域名+https&一个地址配置多个项目【项目实战】

【Linux】Nginx配置域名+https&一个地址配置多个项目【项目实战】

👨‍🎓博主简介 🏅ZEEKLOG博客专家 🏅云计算领域优质创作者 🏅华为云开发者社区专家博主 🏅阿里云开发者社区专家博主 💊交流社区:运维交流社区 欢迎大家的加入! 🐋 希望大家多多支持,我们一起进步!😄 🎉如果文章对你有帮助的话,欢迎 点赞 👍🏻 评论 💬 收藏 ⭐️ 加关注+💗 文章目录 * 前言 * 域名+https配置单个项目 * 域名+https配置多个项目 * 域名不加https配置多个项目 * 本机地址配置多个项目 * 相关文章 * 相关专栏 前言 要使用https,二进制安装编译时需要添加这些参数--with-threads --with-http_ssl_module --with-http_gzip_static_module --with-http_stub_status_module --with-http_v2_module --with-http_realip_module --with-file-aio; ./configure --prefix=/usr/

By Ne0inhk
指尖的诗篇:在Vim的世界里书写代码与梦想,Linux下vim编辑器的使用详解

指尖的诗篇:在Vim的世界里书写代码与梦想,Linux下vim编辑器的使用详解

文章目录 * 前言 * 一、Vim的起源与背景 * 1.1 安装vim * 二、Vim的模式设计:极简而深邃 * 三、vim的强大功能 * 3.1 打开和退出文件 * 3.2 基本编辑操作 * 3.3 移动光标 * 3.4 删除文字 * 3.5 复制 * 3.6 替换 * 3.7 更改 * 3.8 跳至指定的行 * 3.9 撤销上一次操作 * 3.10 查找和替换 * 四、vim的强大功能 * 四、Vim的学习曲线:一段与自我对话的旅程 * 小结 前言 在浩瀚的Linux世界中,

By Ne0inhk
Flutter for OpenHarmony:queue 异步任务队列管理(并发控制与任务调度) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:queue 异步任务队列管理(并发控制与任务调度) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在 Dart 中,异步操作(Future)通常是并发执行的。如果你在一个 for 循环里发起了 100 个网络请求: for(var url in urls){fetch(url);// 瞬间发出100个请求} 这会导致什么? 1. 服务器爆炸:可能触发 API速率限制(429 Too Many Requests)。 2. 客户端OOM:瞬间创建过多的 Socket 连接和 Buffer。 3. UI 卡顿:大量的 Event Loop 任务阻塞。 我们需要一种机制来限制并发数,或者让任务串行执行。 queue

By Ne0inhk
Flutter 三方库 gtin_toolkit 的鸿蒙化适配指南 - 实现全球标准商品条码(GTIN)的正向解析与合法性校检、支持端侧零售与物流供应链扫码实战

Flutter 三方库 gtin_toolkit 的鸿蒙化适配指南 - 实现全球标准商品条码(GTIN)的正向解析与合法性校检、支持端侧零售与物流供应链扫码实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 gtin_toolkit 的鸿蒙化适配指南 - 实现全球标准商品条码(GTIN)的正向解析与合法性校检、支持端侧零售与物流供应链扫码实战 前言 在进行 Flutter for OpenHarmony 的新零售、仓储管理或跨境物流应用开发时,如何准确识别并验证全球通用的商品条码?GTIN(Global Trade Item Number)涵盖了 EAN-13, EAN-8, UPC-A, UPC-E 以及 ITF-14 等多种格式。gtin_toolkit 是一款专为 GTIN 协议处理设计的工具库。它不仅能解析条码,还能计算动态校检位(Check Digit)。本文将介绍如何在鸿蒙端构建极致的条码数据治理能力。 一、原直观解析 / 概念介绍 1.

By Ne0inhk