(单次匹配)完全注释版
#include <bits/stdc++.h> // 包含所有标准库头文件
using namespace std; // 使用标准命名空间,避免重复写std::
// 计算部分匹配表(Partial Match Table)
vector<int> computeLPS(const string &pattern) {
int n = pattern.size(); // 获取模式串长度
vector<int> lps(n, 0); // 初始化LPS数组,全部置0
int len = 0; // 当前最长前缀后缀的长度
// 从模式串的第二个字符开始计算LPS值
for (int i = 1; i < n; ) {
// 如果当前字符匹配成功
if (pattern[i] == pattern[len]) {
len++; // 增加匹配长度
lps[i] = len; // 记录当前LPS值
i++; // 移动到下一个字符
}
else { // 如果匹配失败
if (len != 0) { // 如果之前有部分匹配
len = lps[len - 1]; // 回退到前一个LPS值的位置
}
else { // 完全没有匹配
lps[i] = 0; // LPS值为0
i++; // 移动到下一个字符
}
}
}
return lps; // 返回计算好的LPS数组
}
// KMP搜索算法(单次匹配)
int kmpSearch(const string &text, const string &pattern) {
if (pattern.empty()) return 0; // 空模式串直接返回0
vector<int> lps = computeLPS(pattern); // 计算模式串的LPS表
int i = 0, j = 0; // i:文本串索引, j:模式串索引
while (i < text.size()) {
// 如果当前字符匹配
if (text[i] == pattern[j]) {
i++; // 移动文本串指针
j++; // 移动模式串指针
// 如果完全匹配成功
if (j == pattern.size()) {
return i - j; // 返回匹配的起始位置
}
}
else { // 如果匹配失败
if (j != 0) { // 如果已经匹配了部分字符
j = lps[j - 1]; // 利用LPS表跳过已匹配部分
}
else { // 完全没有匹配
i++; // 只移动文本串指针
}
}
}
return -1; // 没有找到匹配项
}
int main() {
string text = "ABABDABACDABABCABAB"; // 文本串
string pattern = "ABABCABAB"; // 要搜索的模式串
int pos = kmpSearch(text, pattern); // 执行KMP搜索
if (pos != -1) {
cout << "Pattern found at index: " << pos << endl; // 输出匹配位置
} else {
cout << "Pattern not found." << endl; // 输出未找到
}
return 0;
}
(所有匹配)完全注释版
#include <bits/stdc++.h> // 包含所有标准库头文件
using namespace std; // 使用标准命名空间,避免重复写std::
// 计算部分匹配表(Partial Match Table)
vector<int> computeLPS(const string &pattern) {
int n = pattern.size(); // 获取模式串长度
vector<int> lps(n, 0); // 初始化LPS数组,全部置0
int len = 0; // 当前最长前缀后缀的长度
// 从模式串的第二个字符开始计算LPS值
for (int i = 1; i < n; ) {
// 如果当前字符匹配成功
if (pattern[i] == pattern[len]) {
len++; // 增加匹配长度
lps[i] = len; // 记录当前LPS值
i++; // 移动到下一个字符
}
else { // 如果匹配失败
if (len != 0) { // 如果之前有部分匹配
len = lps[len - 1]; // 回退到前一个LPS值的位置
}
else { // 完全没有匹配
lps[i] = 0; // LPS值为0
i++; // 移动到下一个字符
}
}
}
return lps; // 返回计算好的LPS数组
}
// KMP搜索算法(查找所有匹配)
vector<size_t> kmpFindAll(const string &text, const string &pattern) {
vector<size_t> matches; // 存储所有匹配位置的数组
if (pattern.empty()) return matches; // 空模式串直接返回
vector<int> lps = computeLPS(pattern); // 计算模式串的LPS表
int i = 0, j = 0; // i:文本串索引, j:模式串索引
while (i < text.size()) {
// 如果当前字符匹配
if (text[i] == pattern[j]) {
i++; // 移动文本串指针
j++; // 移动模式串指针
// 如果完全匹配成功
if (j == pattern.size()) {
matches.push_back(i - j); // 记录匹配位置
j = lps[j - 1]; // 重置模式串指针继续搜索
}
}
else { // 如果匹配失败
if (j != 0) { // 如果已经匹配了部分字符
j = lps[j - 1]; // 利用LPS表跳过已匹配部分
}
else { // 完全没有匹配
i++; // 只移动文本串指针
}
}
}
return matches; // 返回所有匹配位置
}
int main() {
string text = "ABABDABACDABABCABAB"; // 文本串
string pattern = "ABAB"; // 要搜索的模式串
vector<size_t> matches = kmpFindAll(text, pattern); // 查找所有匹配
// 输出所有匹配位置
for (size_t pos : matches) {
cout << "Found at index: " << pos << endl;
}
return 0;
}
关键点说明:
-
LPS表计算:
- LPS表存储的是模式串每个位置的最长相同前缀后缀长度
- 用于在匹配失败时确定模式串可以跳过多少字符
-
KMP算法核心:
- 利用LPS表避免不必要的回溯
- 时间复杂度为O(n+m),比暴力匹配更高效
-
两种版本区别:
- 单次匹配版本找到第一个匹配就返回
- 所有匹配版本会记录所有匹配位置