字符串匹配(算法导论)

0.KMP

本节图文参考:http://blog.csdn.net/v_july_v/article/details/7041827

失配后,j = next[ j ]

寻找模式串中,各个子串的 { 所有前缀 } & { 所有后缀 } 的最大公共元素长度(前缀和后缀一样,那么失败后就可以直接从后缀开始了)


最大长度值,整体右移一位就成了next数组

next[ j ]=k 的意思是除当前字符 p[ j ]以外的最长前缀后缀(即:失败之后可以从模式串的哪儿开始)



#include<iostream>
#include<string>
#include<vector>
using namespace std;

void getNext(string &sstr,vector<int> &next)
{
	next[0]=-1;//赋值-1,而不是0时,next[0]可以作为哨兵 
	int sl=sstr.size()-1;
	int j=0;
	int k=-1;
	while(j<sl)
	{
		if(k==-1||sstr[j]==sstr[k])//sstr[j]是后缀,sstr[k]是前缀
		{
			++j;
			++k;
			next[j]=k;
		}	
		else
		{
			k=next[k];//寻找更小的最长前缀后缀 
		}
	}
}

bool KMP(string &mstr,string &sstr,vector<int> &next)
{
	int ml=mstr.size();
	int sl=sstr.size();
	int i=0;
	int j=0;
	while(i<ml&&j<sl)
	{
		if(j==-1||mstr[i]==sstr[j])
		{
			i++;
			j++;
		}
		else
		{
			j=next[j];
		}
	}	
	if(j==sl)
	{
		return true;
	}		
	else
	{
		return false;
	}
}

int main()
{
	string mstr="BBCABCDABABCDABCDABDE";
	string sstr="ABCDABD";
	vector<int> next;
	for(int i=0;i<sstr.size();i++)
	{
		next.push_back(-2);
	}
	getNext(sstr,next);
	if(KMP(mstr,sstr,next))
		cout<<"YES!"<<endl;
	else
		cout<<"NO!"<<endl;
} 

关于KMP:http://blog.csdn.net/v_july_v/article/details/7041827真的写得很好

KMP中next数组优化

主要思想是:当sstr[ j ] ! = mstr[ i ]时,如果下个比较值sstr[ next [ j ] ]等于sstr[ j ]的话就不用再和s[ i ]比较了,必然会失配

两张图说明一切


#include<iostream>
#include<string>
#include<vector>
using namespace std;

void getNextval(string &sstr,vector<int> &next)
{
	next[0]=-1;//赋值-1,而不是0时,next[0]可以作为哨兵 
	int sl=sstr.size()-1;
	int j=0;
	int k=-1;
	while(j<sl)
	{
		if(k==-1||sstr[j]==sstr[k])
		{
			++j;
			++k;
			if(sstr[j]!=sstr[k])//如果下述先成立,这句的意思就是sstr[j]!=sstr[next[j]]
			{
				next[j]=k;
			}
			else
			{
				next[j]=next[k];
			}
		}	
		else
		{
			k=next[k];//寻找更小的最长前缀后缀 
		}
	}
}

bool KMP(string &mstr,string &sstr,vector<int> &next)
{
	int ml=mstr.size();
	int sl=sstr.size();
	int i=0;
	int j=0;
	while(i<ml&&j<sl)
	{
		if(j==-1||mstr[i]==sstr[j])
		{
			i++;
			j++;
		}
		else
		{
			j=next[j];
		}
	}	
	if(j==sl)
	{
		return true;
	}		
	else
	{
		return false;
	}
}

int main()
{
	string mstr="BBCABCDABABCDABCDABDE";
	string sstr="ABCDABD";
	vector<int> next;
	for(int i=0;i<sstr.size();i++)
	{
		next.push_back(-2);
	}
	getNextval(sstr,next);
	if(KMP(mstr,sstr,next))
		cout<<"YES!"<<endl;
	else
		cout<<"NO!"<<endl;
} 

1.RabinKarp

主要思想是把大的字符串或者字母字符串等映射成小的、数字字符串,(模式串的话,直接映射成一个数字,这样比对的时候就可以x=y一次比对了,而不用像暴力法一样模式串一位一位的比对)这样就可以相较于暴力法快了。

如果模式串转换后的数字太大,可以先mod一个较大的质数。mod所带来的问题是,有些是伪正确(7%10=7,17%10=7),面对这个问题,只要将正确和伪正确(待选目标)用暴力法验证一下就行了(这时候的暴力法是有目标的,而不是盲目的,所以开销不会像纯粹暴力法那样夸张)。

#include<iostream>
#include<string>
#include<vector>
#include<cmath>
using namespace std;
//为方便理解其核心思想,这里假设文本串T和模式串P都是由0~9的数构成。
//而且保证P转成数字之后,不会超出INT_MAX的范围,故也不再取模运算了 
bool RabinKarp(string &T,string &P,const int &d)
{
	int n=T.size();
	int m=P.size();
	
	int h=pow(d+0.0,m-1);//d^(m-1),类似于存储过程中2^0,2^1,2^2,2^3,2^4一样
	int p=0;//转后不变的模式串 
	int t=0;//转后会变的m长度大小的文本串子串 
	
	for(int i=0;i<m;i++)
	{
		p=d*p+(P[i]-'0');//求模式串的映射值 
		t=d*t+(T[i]-'0');//求初始时文本串m大小的子串(从0~m-1)的映射值 
	} 
	for(int s=0;s<n-m;s++)
	{
		if(p==t)
		{
			return true;
		}
		t=(t-(t/h)*h)*d+(T[m+s]-'0');//向右移一位,更新子串映射值 
	}
	return false;
} 
int main()
{
	string T="258569236589780";
	string P="2365";
	if(RabinKarp(T,P,10))
	{
		cout<<"YES"<<endl;
	}
	else
	{
		cout<<"NO"<<endl;
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值