元素码农
基础
UML建模
数据结构
算法
设计模式
网络
TCP/IP协议
HTTPS安全机制
WebSocket实时通信
数据库
sqlite
postgresql
clickhouse
后端
rust
go
java
php
mysql
redis
mongodb
etcd
nats
zincsearch
前端
浏览器
javascript
typescript
vue3
react
游戏
unity
unreal
C++
C#
Lua
App
android
ios
flutter
react-native
安全
Web安全
测试
软件测试
自动化测试 - Playwright
人工智能
Python
langChain
langGraph
运维
linux
docker
工具
git
svn
🌞
🌙
目录
▶
算法基础
▶
复杂度分析
时间复杂度
空间复杂度
渐进符号
▶
算法思想
分治法
贪心算法
回溯法
概率算法
枚举算法
递推算法
递归算法
动态规划
分支限界法
模拟算法
▶
算法分析
正确性证明
性能优化
算法比较
▶
搜索算法
▶
基本搜索
顺序搜索
二分搜索
插值搜索
▶
图搜索
深度优先搜索
广度优先搜索
启发式搜索
▶
高级搜索
双向搜索
A*算法
模式匹配
▶
排序算法
▶
比较类排序
冒泡排序
快速排序
归并排序
插入排序
选择排序
▶
非比较类排序
计数排序
基数排序
桶排序
▶
高级排序
堆排序
希尔排序
外部排序
拓扑排序
▶
图论算法
▶
图的表示
邻接矩阵
邻接表
边集数组
▶
最短路径
Dijkstra算法
Floyd算法
Bellman-Ford算法
最短路径概述
▶
生成树
Prim算法
Kruskal算法
并查集
最小生成树
▶
图的连通性
强连通分量
割点与桥
双连通分量
欧拉路径
▶
动态规划
▶
基础概念
最优子结构
重叠子问题
状态转移方程
▶
经典问题
背包问题
最长公共子序列
编辑距离
▶
优化技巧
空间优化
状态压缩
区间动态规划
▶
字符串算法
▶
字符串匹配
KMP算法
Boyer-Moore算法
Sunday算法
▶
字符串处理
字典树
后缀数组
字符串哈希
▶
压缩算法
游程编码
Huffman编码
LZ77算法
发布时间:
2025-03-21 20:41
↑
☰
# KMP字符串匹配算法 ## 算法简介 KMP(Knuth-Morris-Pratt)算法是一种高效的字符串匹配算法,由Donald Knuth、James H. Morris和Vaughan Pratt在1977年共同发表。该算法通过利用已经部分匹配的信息,避免了不必要的字符比较,从而提高了匹配效率。 ## 基本原理 ### 朴素匹配的问题 在朴素的字符串匹配算法中,当发生不匹配时,模式串会回退到起始位置重新开始匹配,这导致了许多不必要的比较。例如: ```text 文本串: ABABCABABA 模式串: ABABA 第一轮: ABABA || 匹配两个字符后失败 第二轮: ABABA 完全重新开始 ``` ### KMP的优化思路 KMP算法的核心思想是:当匹配失败时,模式串向右移动的位数不是简单地移动一位,而是根据已经匹配的部分中的内在信息,尽可能地向右移动更大的距离。这是通过构建部分匹配表(next数组)来实现的。 ## 部分匹配表 ### 什么是部分匹配表 部分匹配表记录了模式串中每个位置的最长相等前后缀长度。这个信息用来决定在匹配失败时模式串应该向右移动多远。 例如,对于模式串"ABABA": ```text 位置i: 0 1 2 3 4 字符: A B A B A Next值: 0 0 1 2 3 ``` ### 构建部分匹配表 ```rust fn build_next(pattern: &str) -> Vec<usize> { let pattern = pattern.as_bytes(); let m = pattern.len(); let mut next = vec![0; m]; let mut j = 0; for i in 1..m { while j > 0 && pattern[i] != pattern[j] { j = next[j - 1]; } if pattern[i] == pattern[j] { j += 1; } next[i] = j; } next } ``` ## KMP算法实现 ### 完整代码实现 ```rust fn kmp_search(text: &str, pattern: &str) -> Vec<usize> { let text = text.as_bytes(); let pattern = pattern.as_bytes(); let n = text.len(); let m = pattern.len(); let next = build_next(pattern); let mut matches = Vec::new(); let mut j = 0; for i in 0..n { while j > 0 && text[i] != pattern[j] { j = next[j - 1]; } if text[i] == pattern[j] { j += 1; } if j == m { matches.push(i - m + 1); j = next[j - 1]; } } matches } ``` ### 算法步骤解析 1. 构建部分匹配表(next数组) 2. 初始化匹配位置j为0 3. 遍历文本串: - 如果当前字符不匹配,根据next数组回退j - 如果当前字符匹配,j向前移动 - 如果j达到模式串长度,记录匹配位置 ## 性能分析 ### 时间复杂度 - 构建next数组:O(m),其中m为模式串长度 - 匹配过程:O(n),其中n为文本串长度 - 总体时间复杂度:O(m + n) ### 空间复杂度 - 需要存储next数组:O(m) - 存储匹配结果:O(k),其中k为匹配次数 ## 应用场景 1. 文本编辑器的查找功能 2. 生物信息学中的DNA序列匹配 3. 网络入侵检测系统中的特征码匹配 4. 编译器中的词法分析 ## 与其他算法的比较 ### 相比朴素匹配 - 时间复杂度更优(O(m+n) vs O(mn)) - 实现相对复杂 - 在短文本或简单模式下可能不如朴素算法 ### 相比Boyer-Moore - 实现更简单 - 在某些场景下性能较差 - 内存占用更少 ## 优化建议 1. 对于短模式串(长度<10),考虑使用朴素匹配 2. 可以结合Sunday算法的字符跳转表优化 3. 在特定应用场景下,考虑使用AC自动机等多模式匹配算法 ## 总结 KMP算法是一个经典的字符串匹配算法,它通过巧妙利用已匹配信息来避免不必要的比较。虽然实现相对复杂,但在长文本和复杂模式的匹配中表现出色。在实际应用中,应根据具体场景选择合适的字符串匹配算法。