HDU 2222 Keywords Search

题目大意:如题 AC自动机模板题一道.

思路:自己根据思想码了一遍,各种调试,然后对照原模板再调,,最后发现...差不多拷贝原模板了,嚓,,,,

其实就是建立trie树,然后用过队列建立失败指针,也是KMP思想,利用前缀...

失败指针尽量找深度深的,就是找最近的(我自己理解最近这个名词..)

最后查询的时候,那么如果有路径就一直下,下不去的时候就找父亲的失败指针,看看这个失败指针有没有含有当前字母的儿子.有就对了,没就一直找到根结点查看根结点的儿子

 

AC Program:

#include <vector>
#include <list>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <queue>
#include <cassert>
typedef long long ll;
#define	clr(a)		memset((a),0,sizeof (a))
#define	rep(i,a,b)	for(int i=(a);i<(int)(b);i++)
#define	per(i,a,b)	for(int i=((a)-1);i>=(int)(b);i--)
#define	inf			(0x7fffffff)
#define	eps			1e-6
#define	MAXN		
#define MODN		(1000000007)
using namespace std;
int n;
char key[55];
char des[1000005];
struct node{
   node * next[26];
   node * fail;
   int cnt;
   node(){
     cnt=0;   
     fail=0;
     clr(next);    
   }       
};
//queue<node*>que;
//void init(){
          
//}
node * insert(node * root ){
                            //结点指针 不能每次        
    node * p=root;//工作指针 
    node * q;
    int i=0;
        while(key[i]){
             int index=key[i]-'a';
             //cout<<"index "<<index<<endl; 
             //cout<<"p->next[index] "<<p->next[index]<<endl;
             //还是设置为空的时候申请一个结点,其他的和已经存在的一样,都要移动p和i
             if(!p->next[index]) 
                      p->next[index]=new node();//指针之后还是指针 
             p=p->next[index];//p=q;
             i++;//遍历串 
        }
        p->cnt+=1;           
     
    return root;
}
void ac_auto(node * root){//其实就是建立失败指针 
    root->fail=0;//根结点的失败指针为0
    queue<node*>que;// 弄为全局变量?
                   //que.clear(); 
    que.push(root);
    //工作指针 
   
    while(!que.empty()){
         node * tmp=que.front();
         node * p=0; 
         que.pop();//记得pop 
         rep(i,0,26){
                     //!tmp->next[i]会发生错误 
             if(tmp->next[i]==0)continue;//如果是没有这个字母就跳出 
             if(tmp==root){      //如果根结点的子节点特殊情况处理 
                tmp->next[i]->fail=root;
                que.push(tmp->next[i]); 
                //cout<<"i "<<i<<endl;           
                continue;
             }
             //cout<<"i "<<i<<endl;
             //node * tmp=p;            
             p=tmp->fail;
             //p=p->fail;//tmp当前的是父亲结点,工作的是儿子结点,为其找失败指针 
             while(p!=0){//最远的前缀就是在根结点的子结点才找到相应的前缀 
                  if(p->next[i]!=0){
                      tmp->next[i]->fail=p->next[i];
                      break;                  
                  }//顺着父亲结点的失败指针一直往上找,
                   //如果不是根结点的话,那么这些结点都是和父亲结点有相同的字符
                   //知道找到,或者到跟结点为止.(包括根结点还要判断一次)
                  p=p->fail; 
                          
             } 
             if(p==0)//即是找不到这样的前缀,那么失败指针指向root
                   tmp->next[i]->fail=root;
             que.push(tmp->next[i]);//统一上两种情况的入队姿势 
              //system("pause");  
         } 
         
               
         
    }            
}
void query(node * root){
       node * q=root;  
       int len=strlen(des); 
       int index;
       int sum=0;//最后的结果 
       rep(i,0,len){
            index=des[i]-'a';
            //if(q->next[index]==0)continue;//貌似不能直接跳过,因为还要考虑下q指针 
            while(q->next[index]==0 && q!=root){
                q=q->fail;//如果当前没有这个子结点,那么就根据失败指针倒退,
                //知道找到或者回到根结点                       
            }
            q=q->next[index];
            q=(q==0)?root:q; 
            node * tmp=q;            
            while(tmp!=root && tmp->cnt!=-1){
                sum+=tmp->cnt;
                tmp->cnt=-1;               
                tmp=tmp->fail; 
            }
       }
       cout<<sum<<endl;
}
int main(){
  int test;
  cin>>test;
  while(test--){
      scanf("%d",&n); 
      node * root=new node();
      rep(i,0,n){
         scanf("%s",key);        
         insert(root);
      }  
      scanf("%s",des);
      ac_auto(root);
      query(root);    
  };  
  //system("pause");
  return 0;
}


 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值