字符串匹配

字符串匹配


问题描述

  • 输入两个字符串A和B;

  • 输出字符串B在字符串A中的位置。

示例

  • 输入字符串“ABDABACDBD”和字符串“BACD”;

  • 输出偏移量5,或输出在第一个字符串中“BACD”处的指针。

方法

  • 常用方法有好多:朴素算法Rabin-Karp算法有限自动机算法KMP算法

  • 复杂度对比:复杂度为预处理时间匹配时间的和。

    算法 预处理时间 匹配时间
    朴素算法 0 Ο((n-m+1)*m)
    Rabin-Karp算法 Θ(m) Ο((n-m+1)*m)
    有限自动机算法 Ο(m*字符集个数) Θ(m)
    Knuth-Morris-Prattu算法 Θ(m) Θ(m)
    Boyer–Moore算法 待搜索 待搜索

朴素算法

  • 通过循环找到所有有效偏移;

  • 函数NaiveStringMatcher(char *A,char *T)实现在字符串A中匹配字符串T;

  • 辅助函数CharacterMatcher(char *A,char *B,char *T)判断指针A和B之间字符串是不是和字符串T匹配。

    
    bool CharacterMatcher(char *A,char *B,char *T)
    {
    	char *cc = A;
    	int i = 0;
    	while(cc != B){
    		if(*cc != *(T+i))
    			return false;
    		cc++;
    		i++;
    	}
    	return true;
    }
    void NaiveStringMatcher(char *A,char *T)
    {
    	int iLengthofA = strlen(A);
    	int iLengthofT = strlen(T);
    	for(int i = 0;i <= iLengthofA-iLengthofT ;i++){
    		if(CharacterMatcher(A+i,A+i+iLengthofT,T))
    			cout<<"Pattern occurs with shift"<< i+1 << endl;
    	}
    }
    
    

Rabin-Karp算法1

  • 将字符串做一种数的运算得到一个值,然后进行值的比较;

  • 这个数值要求可以表示出字符的前后顺序,而且可以随时去掉某个字符的值,可以随时添加一个新字符的值。

  • 例如A="12345",B="34",对于B采用运算(3*10+4)得到34作为B的值;采用相同运算方式对A进行运算,然后做比较。

  • 如果相乘过大时,可以对一个较大的素数求模,然后比较。

    
    double Encryption(char *A, int m,int q)
    {
    	int i =0;
    	double sum = 0.0;
    	while(i < m){
    		sum = sum * q + ((int)*(A + i++)) ;
    	}
    	return sum;
    }
    int ChangeEncryption(char *A,int ll,int sum,int q)
    {
    	double mm = pow((double)q,(double)ll - 1);
    	return ((sum - (int)mm * (*A)) * q + *(A + ll));
    }
    char * Rabin_Karp_Algo(char *A,char *B)
    {
    	int ll = strlen(B);
    	double m = Encryption(B ,ll, 128);
    	double n = Encryption(A ,ll, 128);
    	char *cc = A;
    
    	for(;*(cc+ll) != '\0';cc++){
    		if(n == m){
    			return cc;
    		}
    		n = ChangeEncryption(cc,ll,n,128);
    	}
    	return NULL;
    }
    
    

有限自动机算法2

  • 在字符串A中搜索模式T,对于模式T中出现的所有可能情况看作不同状态。

  • 如模式T为“ababaca”

  • 建立状态0~7,分别对应{初始状态}、{a}、{ab}、{aba}、{abab}、{ababa}、{ababac}、{ababaca}。

  • 计算对应状态转移表(计算方法后面讲到)。

    static int delta[8][4]={  //转移函数  检测ababaca
    	//a,b,c,*.
    	{1,0,0,0},  //状态0   {初始状态}
    	{1,2,0,0},  //状态1   {a,}
    	{3,0,0,0},  //状态2   {ab,}
    	{1,4,0,0},  //状态3   {aba,}
    	{5,0,0,0},  //状态4   {abab,}
    	{1,4,6,0},  //状态5   {ababa,}
    	{7,0,0,0},  //状态6   {ababc,}
    	{1,2,0,0}   //状态7   {ababca,}
    };
    int State = 0;  //初试状态
    
  • 函数AutomationMatch(char *A)为匹配函数,返回位置指针。

    
    char *AutomationMatch(char *A)
    {
    	int Length = strlen(A);
    	char *cc = A;
    	while(*cc != '\0'){
    		int n = *cc++ - 'a';
    		if(n != 0 && n != 1 && n != 2)
    			n = 3;
    		State = delta[State][n];
    		if(State == 7)
    			return cc-7;
    	}
    	return NULL;
    }
    
    
  • 状态转移表计算

  • 根据给定模式T[1..m]来计算。

  • 伪代码为

    
    COMPUTE-TRANSITION-FUNCTION(T,E)
    1   m=P.length
    2   for q = 0 to m
    3   	for each charater a∈E
    4   		k = MIN(m+1,q+1)
    5   		repeat
    6   			k = k - 1
    7   		until T[1..k] is tail of T[1..q]&a
    8   		δ(q,a) = k
    9   return δ
    
    
  • c++代码

    
    bool TT(char *T,int k,int q,char a)
    {
    	if(*(T + k-- - 1) != a)
    		return false;
    	while(k > 0 && q > 0){
    		if(*(T + k - 1) != *(T + q - 1))
    			return false;
    		k--;
    		q--;
    	}
    	return true;
    }
    
    int *TransitionFunction(char *T,char *E) //T为搜索模式,E为T的字符集
    {
    
    	const int m = strlen(T);
    	const int n = strlen(E);
    
    	int *Ans[8][3];
    	int q = 0,p = 0;
    	int k = 0;
    
    	for(;q <= m;q++){
    		for(p = 0;p < n;p++){
    			k = MIN(m+1,q+1);
    			while(!TT(T,k,q,*(E+p)) && k>0){
    				k = k-1;
    			}
    			*((int*)Ans + n * q + p) = k;
    		}
    
    	}
    	return (int*)Ans;
    }
    
    

KMP算法3

  • 详细解释看参考

  • 代码

    
    int *ComputePrefixFunction(char *P)
    {
    	int i = 1;
    	int j = 0;
    	int m = strlen(P);
    	int *s = new int[m];
    	memset(s,0,m * 8);
    
    	while(i < m){
    		while(P[i] == P[j] && i < m ){
    			s[i] = s[i-1] + 1;
    			j++;
    			i++;
    		}
    		j = 0;
    		i++;
    	}
    	return s;
    }
    void KMPMatcher(char *T,char *P)
    {
    	int n = strlen(T);
    	int m = strlen(P);
    	int q = 0;
    	int *Map = ComputePrefixFunction(P);
    	int i;
    
    	for(i = 0 ; i < n ; i++){
    		while(q > 0 && P[q] != T[i])
    			q = Map[q-1];
    		if(P[q] == T[i])
    			q++;
    		if(q == m){
    			int gg = i -m +1 ;
    			cout << "zhongduan:" << ends;
    			printf("%d",gg);
    			q = Map[q - 1];
    		}
    	}
    
    }
    

by:狼儿乖乖

time:2015/1/19 20:51:43


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值