初等字符串匹配专题小结[KMP][Manacher][Tire Tree][AC Automation]

来源:互联网 发布:mac没有flash怎么办 编辑:程序博客网 时间:2024/06/03 04:38

刷了3天的字符串匹配题。

为了下面继续切题,小的先小结一些。

字符串匹配的最基础算法是枚举(n^2)。

高深一点的是KMP。

KMP在数据结构课上学过,由于老师只是负责教学,不负责解答他不懂的问题,于是KMP就这么被我搁置一边了。ACM这么多年了,一直不懂这些基础的算法,实在有愧与心。

于是乎专程学习了一下KMP。

先说说KMP的主要思想。KMP用于模式串的匹配。

下面看看一个字符串:(1)AACAACAAB;

我们需要查找的串为:(2)AACAAB;

首先顺序匹配:

(1)AACAACAAB

(2)AACAA

到这里都顺利匹配上了,我们肉眼观察当(2)继续匹配时,'B'和'C'是不匹配的。那么怎样滑动呢?

对于串(2)我们可以发现'B'之前的字符'AA',与串(1)的'C'前面的两个字符是一样的,那么可以这么滑动....

(1)AACAACAAB

(2)        AA

然后继续匹配发现完全匹配了......

好了那么怎么滑动呢?构建一个next数组,记录滑动下标。

可以用一句话来说明:

在J字符的左边有[0,I-1]与[J-I-1,J-1]相同的话,下次J失配时,就可以滑动到I。

因为是滑动到J才失配,也就是说,J之前的所有字符串都是和主串相匹配的。由此,只要在本串中找到前缀和主串相匹配的(一定是部分匹配)选择滑动就可以了。

下面是构建next的函数。T是模式串

void setNext(){     int j=0,k=-1;     next[0]=-1;     while( j<lenT )     {            if( k==-1||t[j]==t[k] )                next[++j]=++k;            else                k=next[k];     }}
当失配则回退,匹配则赋值继续前进。

下面是KMP的匹配模版

int kmp(){    int i=0,j=0;    cnt=0;    while( i<lenS&&j<lenT )    {           if( j==-1 || s[i]==t[j] )               i++,j++;           else               j=next[j];    }    if( j>lenT )return i-lenT;    elsereturn -1;}
匹配则继续,失配则滑动。

KMP主要用来解决的问题

1.主串中模式串出现的位置

2.主串中出现模式串的次数

3.主串分割成多少个模式串

4.模式串中前缀的循环次数

以上为KMP.......  写得不好啊........


好了接下来Manacher;

这个算法主要是用于计算回文串。运用了回文串的性质。

假设我们有一个回文串以id为中心,p[id]为以id为中心的回文串的半径。

下面给一个回文串:

             id  p[id]

   |<----|----->|

CABAAKAABAA

可以看出回文串为ABAAKAABA中心为K。

好了我们以mx=id+p[id],以id为中心的回文字符串的最右控制范围。

现在看K的右边那个字符串'B'。这个B实在mx之内的,所以还是受到了id的控制!

所以这个B的性质与B关于id的对称点左边的B有关。为啥?因为是回文嘛~两边对称。

通过肉眼,p['B']=1;所以右边这个B的左右两边也和左边的B相似。

但是仅限于当右边的B的右边界还在mx内时。

为啥?

看下面

AABAAKAABAC

这个字符串左边的p[B]=2;而右边的B显然没有这么广的控制范围,因为超过了mx的控制范围了。

所以右边p[B]的控制范围在与右边界的距离,和对称点的控制范围内取一个最小的就可以了。

但是对于原来的字符串,右边的B实际范围是还可以拓展的。所以继续拓展就好了。

拓展完后会发现新的回文串的最右边界超过了mx,此时记录更新就好了。

从左到右扫完后,取出最大的p[id]就好。再处理一下就好了。

为了避免判断奇偶性,在串中插入不常用字符'#','$'什么的.... 就这样。

#include<iostream>#include<cstdio>#include<string.h>using namespace std;int p[2222222];char str[1111111],str1[2222222];int len;void Init(){  str1[0]='$';  str1[1]='#';  len=2;  for( int i=0;str[i]!=0;i++ )  {    str1[len++]=str[i];    str1[len++]='#'; } str1[len]=0;}int main(){ int T=0; while( scanf("%s",&str)!=EOF ) {    if( strlen(str)==3 && str[0]=='E' && str[1]=='N' && str[2]=='D' )       break;    //memset( str1,0,sizeof(str1) );    memset( p,0,sizeof(p) );    Init();    int id,mx=0;    for( int i=1;i<len;i++ )    {   if( mx>i )   p[i]=min(p[(id<<1)-i],mx-i);   else   p[i]=1;   while( str1[i-p[i]]==str1[i+p[i]] )      p[i]++;    if( mx<i+p[i] );{ mx=i+p[i]; id=i; }      }      printf( "Case %d: ",++T );      int ans=0;      for( int i=1;i<len;i++ )      ans=max(p[i],ans);      printf( "%d\n",ans-1 );  } return 0;}

好吧下面继续讲==

所谓TireTree就是字典树,字母树。从根节点开始,每个节点代表一个字母,单词的第K个字母在树的第K层。

这只是一种数据结构。实现也不难。但却是后缀树与AC自动机的基础。

不多说,直接上模版。

#include<iostream>#include<string>#include<cstdio>#define MAX 10using namespace std;char s[11111][11];int allocp;struct TireNode{       int nCount;       TireNode *next[MAX];};TireNode Memeroy[1111111];void InitTire( TireNode **root ){     *root=NULL;}TireNode *CreateTire(){         int i;         TireNode *p=&Memeroy[allocp++];         p->nCount=1;         for( int i=0;i<MAX;i++ )              p->next[i]=NULL;         return p;}void InsertTire( TireNode **root,char *s ){     int i=0,k;     TireNode *p;     if( !(p=*root) )         p=*root=CreateTire();          while( s[i] )     {            k=s[i++]-'0';            if( p->next[k] )                p->next[k]->nCount++;            else                p->next[k]=CreateTire();            p=p->next[k];      }}bool SearchTire( TireNode **root,char *s ){     int i=0,k;     TireNode *p=*root;     int cnt=0;      while( s[i] )     {            k=s[i++]-'0';            cnt=p->next[k]->nCount;             p=p->next[k];         }     if( cnt==1 )         return false;     else         return true; }int main(){    int T;    scanf( "%d",&T );    while( T-- )    {           allocp=0;           TireNode *root;           root=NULL;           int len=0;           scanf( "%d",&len );            for( int i=0;i<len;i++ )           {                scanf( "%s",&s[i] );                InsertTire(&root,s[i]);           }           bool found=true;           for( int i=0;i<len;i++ )           {                if( SearchTire(&root,s[i]) )                {    found=false;                    break;}           }           if( found==false )               printf( "NO\n" );           else               printf( "YES\n" );     }    return 0;}
累了。。。。AC自动机明天在写吧。。。

可以这么理解AC自动机就是在一棵字典树树上进行KMP......

先模版之.......

#include<iostream>#include<cstdio>#include<string.h>#define MAX 26using namespace std;int root,tot;struct node{       int fail;       int cnt;       int next[MAX];       void init()   {            memset( next,0,sizeof(next) );            fail=-1;cnt=0;       }}Tire[5555555];int queue[5555555];void init(){     root=tot=0;     Tire[root].init();}void insert( int root,char *s ){     int p=root;     int i=0,k;     while( s[i] ) {            k=s[i++]-'a';            if( !Tire[p].next[k] )            {                Tire[++tot].init();                Tire[p].next[k]=tot;            }            p=Tire[p].next[k];     }     Tire[p].cnt++;}void build_ac_automation(){     int head,tail;     head=tail=0;     queue[tail++]=root;     while( head<tail ) {            int cur=queue[head++];            for( int i=0;i<MAX;i++ ){                 if( Tire[cur].next[i] ) {                     int son=Tire[cur].next[i];                     int p=Tire[cur].fail;                     if( cur==root )                         Tire[son].fail=root;                     else                         Tire[son].fail=Tire[p].next[i];                     queue[tail++]=son;                 }                 else {                     int p=Tire[cur].fail;                     if( cur==root )                         Tire[cur].next[i]=root;                     else                         Tire[cur].next[i]=Tire[p].next[i];                 }            }     }}int query( char *s ){    int i=0,k,p=root;    int ret=0;    while( s[i] )    {           k=s[i++]-'a';           while( !Tire[p].next[k]&&p!=root )                 p=Tire[p].fail;           p=Tire[p].next[k];           if(p==-1)p=0;           int temp=p;           while( temp!=root&&Tire[temp].cnt!=-1 )           {                  ret+=Tire[temp].cnt;                  Tire[temp].cnt=-1;                  //sTire[temp].cnt=0;                  temp=Tire[temp].fail;           }    }    return ret;}char str[1111111];int main(){    int T;    scanf( "%d",&T );    while( T-- ){           init();           int N;           scanf( "%d",&N );           while( N-- )           {                 scanf( "%s",&str );                 insert( root,str );           }           build_ac_automation();           scanf( "%s",&str );           printf( "%d\n",query(str) );           //system("pause");    }    return 0;}











原创粉丝点击