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); }