算法导论 第三十三章:字符串匹配

朴素字符串匹配算法

 用一个循环来找出所有有效位移,该循环对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;
		
	}

运行结果:



【注:若有错误,请指正~~~】



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值