朴素字符串匹配算法
用一个循环来找出所有有效位移,该循环对n-m+1个可能的每一个s值检查条件P[1...m]=T[s+1...s+m]。
伪代码如下:
EG:
Rabin-Karp 算法
已知一个模式P[1...m],设p表示其相应的十进制数的值。类似地,对于给定的文本T[1...n],用ts来表示长度为m的子字符串T[s+1...s+m] (s=0,1,...,n-m)
当且仅当ts =p时,T[s+1...s+m]=P[1...m]。
利用霍纳法则(Horner's rule),在Θ(m)时间内计算p的值:
p=P[m]+10(P[m-1]+10(P[m-2]+...+10(P[2]+10P[1])...))
类似地,也可以在Θ(m)的时间内,根据T[1...m]计算出t0的值。
为了在Θ(n-m)时间内计算剩余的值t1,t2,...tn-m,可以在常数时间内根据ts计算出 ts+1,因为:
在一般情况下,采用d进制的字母表{0,1,...,d-1}时,所选取的q要满足使dq的值在一个计算机字长内,并调用如下递归式以使对模q进行运算,使其成为:
其中,是一个m数位文本窗口中高位数字上的数字“1”的值。
伪代码如下:
EG:
Knuth-Morris-Pratt 算法
该算法需要先计算模式P的前缀函数,通过前缀函数,课实现跳跃式去匹配文本字符串。前缀函数计算如下:
已知一个模式P[1...m],模式P的前缀函数是函数π:{1,2,...,m}→{0,1,...m-1}并满足:
伪代码:
EG:
计算前缀:
匹配:
运行时间比较:
=================================================================================================================================
三种算法完整代码如下:
#include<iostream>
#include<string>
using namespace std;
void NativeStrMatcher(string s,string p)
{
int n=s.length();
int m=p.length();
for(int i=0;i<n-m;i++)
{
string ss=s.substr(i,m);
if(p==ss) // P[1...m]=S[i+1...i+m]
cout<<"Matching Successfully,the matching index is:"<<i<<endl;
}
}
void RabinKarp_StrMacher(string T, string P, int d, int q)
{
int n=T.length();
int m=P.length();
int h=1;
int p=0; //denote the corresponding radix_d value of P[1...m]
int t=0; //........................................of T[s+1...s+m]
for(int i=0;i<m-1;i++)
h=(h*d)%q; //h=d^(m-1) mod q
//Preprocessing:compute the value of p & t
for(int i=0;i<m;i++)
{
p=(p*d+(P[i]-'a'))%q;
t=(t*d+(T[i]-'a'))%q;
}
//Matching
for(int s=0;s<n-m;s++){
if(p==t)
if(P==T.substr(s,m))
cout<<"Matching Successfully,the matching index is:"<<s<<endl;
int tmp=t;
t=(d*(tmp-(T[s]-'a')*h)+(T[s+m]-'a'))%q;
}
}
void computePrefixFunc(string P,int *preArray)
{
int m=P.length();
preArray[0]=0;
int k=0;
for(int q=1;q<m;q++)
{
while(k>0 && P[k]!=P[q])
k=preArray[k-1];
if(P[k]==P[q])
k++;
preArray[q]=k;
}
cout<<"The prefix array is:"<<endl;
for(int i=0;i<m;i++)
cout<<preArray[i]<<" ";
cout<<endl;
}
void KMP_StrMatcher(string T,string P)
{
int n=T.length();
int m=P.length();
int *PValue=new int[m];
computePrefixFunc(P,PValue);
int q=0;
for(int i=0;i<n;i++)
{
while(q>0 && P[q]!=T[i])
q=PValue[q-1];
if(P[q]==T[i]);
q++;
if(q==m-1){
cout<<"Matching successfully,the matching index is:"<<i<<endl;
q=PValue[q];
}
}
delete[] PValue;
}
int main()
{
string str0="ababcabcacbab";
string str1="abcac";
cout<<"-----------Native Algorithm-------------------"<<endl;
NativeStrMatcher(str0,str1);
cout<<"-----------Rabin-Karp Algorithm----------------"<<endl;
int d=3;
int q=13;
RabinKarp_StrMacher(str0,str1,d,q);
cout<<"----------Knuth-Morris-Pratt Algorithm----------"<<endl;
KMP_StrMatcher(str0,str1);
return 0;
}
运行结果:
【注:若有错误,请指正~~~】