KMP算法基本思想:
我们在用常规的思想做 字符串匹配时候是 如 对如 字符如果 T = abab 用P = ba 去匹配,常规思路是 看 T 第一个元素 a 是否 和P 的一个 b 匹配 ,匹配的话 查看各自的第二个元素,不匹配 则将 T 串的 第二个元素开始 和 P 的第一个匹配,如此 一步一步 的后移 来进行逐个匹配 这样效率不是很高,对于 KMP 算法,其根本就是对 匹配模式串 P 进行 预处理当在匹配的时候可以 选择 联系向 T 后移动几步进行匹配,而不需只是逐步移动》
如 对于 用 P[1:m] 去匹配 T[1:n ] 当 p[1:i ] 与 T[1+s : j] ( j = i+s) 匹配 ,但是 T[j+1] != P[i+1] 如此 ,我们必须将 i+s 的 T 起始匹配位置 向后移动,(常规是移动一步进行匹配),如果我们知道 对于 P 的已经匹配子串 p[1:i] 有 前 q 个元素 可以是 p[1:i]的前缀,也是 串 p[1:i] 的后缀那么 我们可以 从 P 的 i+1 的位置开始,和 j+1 的位置开始匹配,也就是所 可以向后移动 i-q 步如后进行匹配。
详情看算法导论:P568
代码如下:
#include <iostream>
#include<string>
#include<malloc.h>
using namespace std;
int fail[100];
int *pre;
// 预处理 函数
void prefix_function(const char const *p,int m){
int k = 0; // k 表示 匹配 P 的 前 k 个元素 可以 作为 匹配 P 的后缀 也作为需要匹配的位置
pre[0] = 0;
pre[1] = 0 ; // 第一个元素 有 0 个元素 与其 后缀匹配
for ( int q = 2; q<= m; q ++){
while (k >0 && p[k] != p[q-1]){
k = pre[k];
}
if( p[k] == p[q-1]){
k++; // k 匹配的个数 加一个
}
pre[q] = k;
}
for( int i =1 ;i <=m ;i++){
cout<<pre[i]<<" ";
}
cout<<endl;
}
int kmp_match(const char *t,const char *p,int n, int m){
int q = 0 ; // q 表示 在 匹配 p 在 t 中 匹配的个数
prefix_function(p,m);
for( int i = 0 ; i<n; i++){
while ( q > 0 && t[i] != p[q] ){ // 如果已经 匹配的个数 大于 0 并且
q = pre[q];
}
if( t[i] == p[q]){
q = q+1;
}
if( q == m){
cout<< i - m<<endl;
q = pre[q];
// return i-m;
}
}
return -1;
}
int kmp(const char* str, const char* pat){
int i, j, k;
memset(fail, -1, sizeof(fail));
for (i = 1; pat[i]; ++i) {
for (k=fail[i-1]; k>=0 && pat[i]!=pat[k+1];
k=fail[k]);
if (pat[k + 1] == pat[i]) fail[i] = k + 1;
}
i = j = 0;
while( str[i] && pat[j] ){ // By Fandywang
if( pat[j] == str[i] ) ++i, ++j;
else if(j == 0)++i;//第一个字符匹配失败,从str下个字符开始
else j = fail[j-1]+1; }
if( pat[j] ) return -1;
else return i-j;
}
int main (void) {
char const *t;
char const *p;
int n,m;
string t1;
string p1;
cin>>t1>>p1;
n = t1.length();
m = p1.length();
t = t1.c_str();
p = p1.c_str();
pre = new int[m+1];
cout<<n<<" "<<m<<endl;
cout<<kmp_match(t,p,n,m)<<endl;
//cout<<kmp(t,p);
}