kmp算法详解及c++代码示例

KMP(Knuth-Morris-Pratt)算法是一种改进的字符串匹配算法,用于解决在一个文本串S内查找一个词串P的出现位置的问题。与朴素的暴力匹配不同,KMP算法可以在匹配失败时利用已经部分匹配的有效信息,避免从头开始匹配,从而提高匹配效率。

原理

KMP算法的核心是利用一个“部分匹配表”(也称为“失败函数表”或“跳转表”),该表记录了词串P中各个位置上的字符,与词串P的某个前缀能够匹配的最长长度。在匹配过程中,当发生不匹配时,可以利用这个表来确定下一步应该从哪里开始匹配,从而避免不必要的比较。

部分匹配表的构造
  1. 初始化一个空表。
  2. 对于词串P的每个位置i(从1开始),计算以该位置结尾的子串的前缀和后缀的最长公共元素长度,并将这个值记录在表中对应的位置。
  3. 如果不存在这样的公共元素,则对应位置的值为0。
匹配过程
  1. 将词串P与文本串S的第一个字符对齐。
  2. 从左到右逐个比较P和S的字符。
  3. 如果出现不匹配,查找部分匹配表中当前位置的值,将P向前滑动这个值的距离,然后从新的位置开始继续匹配。
  4. 如果全部匹配成功,返回词串P在文本串S中的起始位置。

实例

假设词串P为"ABCDABD",文本串S为"ABCABABDABCDABDABCDABABCAB"。

  1. 构造部分匹配表:
    • A的后缀和前缀无公共元素:0
    • AB的后缀B和前缀B相同:1
    • ABC的后缀BC和前缀无公共元素:0
    • ABCD的后缀CD和前缀无公共元素:0
    • ABCDA的后缀DA和前缀A相同:1
    • ABCDAB的后缀AB和前缀AB相同:2
    • 因此,部分匹配表为[0, 1, 0, 0, 1, 2]。
  2. 匹配过程:
    • 将P与S的第一个字符对齐。
    • 比较到P的第四个字符D时,发现不匹配。
    • 查找部分匹配表中位置4的值,为0,因此P不动,S向右移动一个字符。
    • 继续比较,直到P全部匹配成功,或P移动到S的末尾。

应用场景

KMP算法在字符串匹配、搜索引擎、文本编辑器、生物信息学等领域有广泛应用。例如,在搜索引擎中,KMP算法可以快速定位网页中关键词的出现位置,从而提高搜索效率;在文本编辑器中,KMP算法可以实现高效的查找和替换功能;在生物信息学中,KMP算法可以用于DNA序列的比对和分析等。

总的来说,KMP算法通过充分利用已经匹配的信息,减少了不必要的比较次数,从而提高了字符串匹配的效率

KMP算法C++代码示例

 

cpp复制代码

#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 计算部分匹配表
vector<int> computeLPSArray(const string& pat, int M) {
vector<int> lps(M, 0);
int len = 0; // length of the previous longest prefix suffix
int i = 1;
lps[0] = 0; // lps[0] is always 0
// loop to calculate lps[i] for i = 1 to M-1
while (i < M) {
if (pat[i] == pat[len]) {
len++;
lps[i] = len;
i++;
} else {
// (pat[i] != pat[len])
if (len != 0) {
// This is tricky. Consider the example.
// AAACAAAA and i = 7. The idea is similar
// to search step.
len = lps[len - 1];
// Also, note that we do not increment i here
} else {
// if (len == 0)
lps[i] = 0;
i++;
}
}
}
return lps;
}
// KMP搜索函数
int KMPSearch(const string& pat, const string& txt) {
int M = pat.length();
int N = txt.length();
// 创建LPS数组
vector<int> lps = computeLPSArray(pat, M);
int i = 0; // index for txt[]
int j = 0; // index for pat[]
while (i < N) {
if (pat[j] == txt[i]) {
j++;
i++;
}
if (j == M) {
cout << "Found pattern at index " << i - j << endl;
j = lps[j - 1];
} else if (i < N && pat[j] != txt[i]) {
// Mismatch after j matches
if (j != 0)
j = lps[j - 1];
else
i = i + 1;
}
}
return 0;
}
int main() {
string txt = "ABABDABACDABABCABAB";
string pat = "ABABCABAB";
KMPSearch(pat, txt);
return 0;
}

时空复杂度分析

时间复杂度
  • 部分匹配表计算 (computeLPSArray): 这个函数的时间复杂度是O(M),其中M是模式串的长度。因为我们需要遍历整个模式串一次来计算LPS数组。
  • KMP搜索 (KMPSearch): 这个函数的时间复杂度是O(N),其中N是文本串的长度。在最坏情况下,我们可能需要遍历整个文本串一次。尽管在每次不匹配时,我们都会使用LPS数组来跳过一些字符,但总体上,我们仍然需要遍历文本串的每个字符至少一次。

因此,总的时间复杂度是O(M + N)。

空间复杂度
  • 我们使用了一个大小为M的LPS数组来存储部分匹配表。因此,空间复杂度是O(M)。

请注意,这里的时空复杂度分析是基于理想情况的。在实际应用中,由于缓存、内存访问模式和其他因素,实际性能可能会有所不同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值