字符串匹配算法

问题:

  两个字符串,A=”abcababbca“,B=”abab“,求问字符串B是否在字符串A中。这里依旧分两种情况讨论,一是字符串B在A中的位置必须是连续的。二是字符串B在A中的位置不是连续的,只要相对位置正确就可以。

(1)字符串B在A中的位置是连续的。

方法一:KMP匹配算法
  KMP匹配算法的原理就是在匹配过程中A的指针一直往前顺次走,而B的指针尽量往前走。我们称B为模式串,在模式串中,每个字符都有一个next值,当与A
匹配过程中,若发现不匹配,则A指针不动,B指针指向当前B中不匹配的那个字符的next值位置的字符,即B指针先前移动。而next值得求的是按照后缀和前缀的概念来的,如B="abab"中的每个字符的next值为:(关于如何求取的具体细节在这就不详细叙说了,可以看严蔚敏版数据结构,里面有详细说明)
      下标:1  2  3  4
      字符:a  b  a   b
         next值:0  1  1   2
具体实现见代码:

View Code
#include <iostream>
using namespace std;

void CreatNext(char *C,int *N,int len);
int KMP(char *A,int lenA,char *B,int *N,int lenB);

int main(int argc,char *argv[])
{
char *A="0abcabaabca";
char *B="0abaa";
int *N=new int[7];
int lenA=10,lenB=4;

CreatNext(B,N,lenB);
int index=KMP(A,lenA,B,N,lenB);
cout<<index<<endl;

delete []N;
return 0;
}

void CreatNext(char *C,int *N,int len)
{
int i=1,j=0;
N[1]=0;
while(i<len)
{
if(j==0||C[i]==C[j])
{
i++;
j++;
N[i]=j;
}
else
j=N[j];
}
}
int KMP(char *A,int lenA,char *B,int *N,int lenB)
{
int i=1,j=1;
while(i<=lenA&&j<=lenB)
{
if(j==0||A[i]==B[j])
{
i++;
j++;
}
else
j=N[j];
}
if(j>lenB)
return i-lenB-1;
else
return -1;
}

方法二:有限状态自动机
  用有限状态自动机解决字符串匹配问题,是个很创新的方法,这里重点说一下,首先说一下有限状态自动机的概念。
  一个有限状态自动机M是一个5元组,即M=(Q,q0,A,∑,δ),其中:
    Q:状态的有限集合     q0:初始状态     A:可接受状态    ∑:有限输入空间    δ:转移函数,即每输入一个值,将其转换为不同的状态
  这样说,可能会有些抽象,也用上面的例子为证,B=“abab”,共4个字母,则可以设Q的状态空间为{0,1,2,3,4} ;q0={0};∑为字母表,即26个字母;δ转移函数如图表表示:
        状态    输入字母 
           a   b   其他字母
         0    1   0     0
         1    1   2     0
         2    3   0     0
         3    1   4     0
根据这个图表,当输入字符串A="abcababbca"时,初始状态是0,输入A[1]='a',变为状态1,接着输入A[2]=b,变为状态2,输入A[3]=c,变为状态0,输入A[4]=a,
变为状态1,A[5]=b,变为状态2,A[6]=a,变为状态3,A[7]=b,变为状态4,则找到匹配。所以有限状态自动机的关键是转移函数的求取,其实仔细观察也是后缀和前缀的问题,具体见代码:(关于有限状态自动机应用在字符串匹配,可具体见算法导论第32章“字符串匹配”)

View Code
#include <iostream>
using namespace std;
int T[8][27];

int Function(char *A,char *B,int len);
void Translation(char *A,int len);

int main(int argc,char *argv[])
{
char *A="0abab";
char *B="0abcababbca";
Translation(A,4);
int s=0;
for(int i=1;i<=10;i++)
{
int index=B[i]-'a';
s=T[s][index];
if(s==4)
cout<<i-4<<endl;
}
return 0;
}

int Function(char *A,char *B,int len)
{
for(int k=len;k>=1;k--)
{
for(int i=1;i<=k;i++)
{
if(A[i]!=B[i+len-k])
break;
}
if(i>k)
return k;
}
return 0;
}
void Translation(char *A,int len)
{
char *q=new char[len+2];
q[0]='0';
memset(T,0,8*27);
for(int i=1;i<=len;i++)
{
for(int j=0;j<26;j++)
{
char c='a'+j;
q[i]=c;
T[i-1][j]=Function(A,q,i);
}
q[i]=A[i];
}
}

(2)字符串B在A中的位置是不连续的
  这里也是用的有限状态自动机的方法,转移函数见图表

        状态    输入字母 
           a   b   其他字母
         0    1   0     0
         1    1   2     1
         2    3   2     2
         3    3   4     3

代码:(该方法不是最优的,纯粹是为了练习有限状态自动机)

View Code
#include <iostream>
#include <queue>
using namespace std;

int stage[6][26];
void Translate(char *S,int len); //求取状态转换函数
int main(int argc,char *argv[])
{
char *A="0abcabaabca";
char *B="0abaa";
Translate(B,4);
int s=0;
queue<int> Q;
for(int i=1;i<=10;i++)
{
int index=A[i]-'a';
int temp=s;
s=stage[s][index];
if(s!=temp) //记录状态改变的是下标
Q.push(i);
if(s==4)
{
cout<<"find"<<endl;
while(!Q.empty())
{
cout<<Q.front()<<"";
Q.pop();
}
cout<<endl;
return 0;
}
}
cout<<"not find"<<endl;
return 0;
}

void Translate(char *B,int len)
{
memset(stage,0,6*26);
for(int i=0;i<len;i++)
for(int j=0;j<26;j++)
{
if((j+'a')==B[i+1])
stage[i][j]=i+1;
else
stage[i][j]=i;
}
return;
}

 

posted @ 2011-11-19 16:38  东方雨中漫步者  阅读(576)  评论(0编辑  收藏  举报