-
kmp算法
KMP算法是一种改进的
字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为
克努特——
莫里斯——
普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。
时间复杂度O(m+n)。
基本思想
设主串(下文中我们称作T)为:a b a c a a b a c a b a c a b a a b b
模式串(下文中我们称作W)为:a b a c a b
用暴力算法匹配字符串过程中,我们会把T[0] 跟 W[0] 匹配,如果相同则匹配下一个字符,直到出现不相同的情况,此时我们会丢弃前面的匹配信息,然后把T[1] 跟 W[0]匹配,循环进行,直到主串结束,或者出现匹配成功的情况。这种丢弃前面的匹配信息的方法,极大地降低了匹配效率。
而在KMP算法中,对于每一个模式串我们会事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数。
在第一次匹配过程中
T: a b a c a
a b a c a b a c a b a a b b
W: a b a c a
b
在T[5]与W[5]出现了不匹配,而T[0]~T[4]是匹配的,现在T[0]~T[4]就是上文中说的
已经匹配的模式串子串,现在移动找出
最长的相同的前缀和后缀并使他们重叠:
T: a b a c a
a b a c a b a c a b a a b b
W: a
b a c a
b
然后在从上次匹配失败的地方进行匹配,这样就减少了匹配次数,增加了效率。
然而,如果每次都要计算
最长的相同的前缀反而会浪费时间,所以对于模式串来说,我们会提前计算出每个匹配失败的位置应该移动的距离,花费的时间就成了常数时间。比如:
j | 0 | 1 | 2 | 3 | 4 | 5 |
W[j] | a | b | a | c | a | b |
F(j) | 0 | 0 | 1 | 0 | 1 | 2 |
当W[j]与T[j]不匹配的时候,设置j = F(j-1).
C++源代码:
#include<iostream>
#include<stdlib.h>
#include<vector>
using namespace std;
inline void NEXT(const string&T, vector<int>&next){//按模式串生成vector,next(T.size())
next[0] = -1;
for (int i = 1; i<T.size(); i++){
int j = next[i - 1];
while (j >= 0 && T[i - 1] != T[j]) j = next[j];//递推计算
if (j >= 0 && T[i - 1] == T[j]) next[i] = j + 1;
else next[i] = 0;
}
}
inline string::size_type COUNT_KMP(const string&S, const string&T){
//利用模式串T的next函数求T在主串S中的个数count的KMP算法
//其中T非空,
vector<int>next(T.size());
NEXT(T, next);
string::size_type index, count = 0;
for (index = 0; index<S.size(); ++index){
int pos = 0;
string::size_type iter = index;
while (pos<T.size() && iter<S.size()){
if (S[iter] == T[pos]){ ++iter; ++pos; }
else{
if (pos == 0) ++iter;
else pos = next[pos - 1] + 1;
}
}
if (pos == T.size() && (iter - index) == T.size()) ++count;
}
return count;
}
int main(int argc, char*argv[])
{
string S="abaabcacabaabcacabaabcacabaabcacabaabcac";
string T="ab";
//cin >> S;
//cin >> T;
string::size_type count = COUNT_KMP(S, T);
cout << count << endl;
system("PAUSE");
return 0;
}