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

原创 2012年03月24日 22:28:42

刷了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;
    else
	return -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 10
using 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 26
using 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;
}











版权声明:本文为博主原创文章,转载请写明出处。

KMP字符串匹配(初学者必看,讲的很清晰)

从头到尾彻底理解KMP 首先声明一下,本博文转自July的博客,之前看了很多关于KMP算法的博客,发现都没讲清楚,但看了July的博客后,我就对KMP算法有了一个很清楚的...
  • luoshengkim
  • luoshengkim
  • 2015年03月29日 10:34
  • 2775

字符串匹配算法KMP Java实现

kmp算法的核心思想:先对搜索字串生成偏移对照表,匹配时从左向右依次比较(bm从右向左,号称比kmp更快),相等则文档和搜索字串的下标+1迭代,否则查表,定位最优的偏移位置(文档下标不变,搜索字串下标...
  • a386347993
  • a386347993
  • 2015年01月09日 10:49
  • 1032

史上最浅显易懂的KMP算法讲解:字符串匹配算法

KMP算法是一种改进后的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此人们称它为克努特-莫里斯-普拉特操作(简称KMP算法)。 KMP算法又称“看毛片”算...
  • wangbaochu
  • wangbaochu
  • 2016年02月18日 20:32
  • 9229

字符串匹配选讲(KMP Trie树 manacher)PPt

  • 2017年08月25日 19:27
  • 1.97MB
  • 下载

字符串专题(trie,KMP,AC自动机,manacher)

字符串博大精深,而且算法都比较难以理解(不像图论那么显然) 最近开始钻研大白字符串,顺便看各种blog和刷kuangbin专题 首先是trie树(字典树),这个是最基础的(据说也很重要) trie树b...
  • Miracle_ma
  • Miracle_ma
  • 2015年08月20日 12:59
  • 543

字符串匹配的三个算法(KMP+字典树+AC自动机)

字符串匹配算法,KMP,字典树,AC自动机。 分别对应一对一匹配,一对多匹配,多对多匹配。...
  • qq_30346729
  • qq_30346729
  • 2017年12月18日 17:13
  • 129

字符串匹配:KMP & AC 自动机个人学习总结

字符串匹配问题             给定两个字符串,其中一个文本串T[1..n], 另一个是模式串P[1...m]。确定T中是否存在与P串完全相等的(连续的)子串,若有,输出T中和P完...
  • Night__elf
  • Night__elf
  • 2012年12月27日 09:55
  • 673

高效面试之字符串匹配(KMP,AC算法)

文本Tn  模式Pm, P在T中出现的位置为偏移 字符串匹配问题描述为:找出所有偏移s(0= 分两步完成,预处理+匹配 算法 预处理时间 匹配...
  • cqkxboy168
  • cqkxboy168
  • 2014年10月26日 01:44
  • 1057

字符串匹配的KMP算法浅析

  • 2014年08月26日 14:51
  • 139KB
  • 下载

kmp字符串匹配

  • 2013年07月15日 21:39
  • 30KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:初等字符串匹配专题小结[KMP][Manacher][Tire Tree][AC Automation]
举报原因:
原因补充:

(最多只允许输入30个字)