AC自动机(KMP+字典树)

AC自动机(KMP+字典树)
题目:输入N个串,判断有多少个搜索串的子串
In				out
1				4
7
a
ab
abc
abcd
abcde
abcdef
abcdefg
abcd
#include<bits/stdc++.h>
using namespace std;
char str[1000000+100];
struct node{
    int count;					//当前结点有多少个字符串共同享用
    struct node *next[26];		//指向26个字母的指针
    struct node *fail;			//失配指针
    void init(){				//构造函数
        for(int i = 0; i < 26; i++) next[i] = NULL;
        count = 0;
        fail = NULL;
    }
} *root;
void insert(){
    int len, k;							//开始建树
    node *p = root;					//先把指针指向根
    len = strlen(str);					//求出串长
    for(k = 0; k < len; k++){				//遍历一次字符串
        int pos = str[k] - 'a';				//读出当前字符在字母表中的位置
        if( p->next[pos] == NULL ){		//如果为空则开辟新结点,初始化,P指向往下指
            p->next[pos] = new node;
            p->next[pos]->init();
            p = p->next[pos];
        }
        else							//即使忆开辟,也要往下指
            p = p->next[pos];
    }
    p->count++;						//当前结点使用次数加一,注意是最后字符的结点才加!!
}
void getfail()
{
   int i;
   node *p = root, *son, *temp;				//指向根的指针,儿子指针,临时指针
   queue <struct node *> que;				//广搜
   que.push(p);							//把根压入队列
   while( !que.empty() ){					//只要队列不为空就就循环
       temp = que.front();					//读出最前面的结点
       que.pop();							//从队列删去
       for(i = 0; i < 26; i++){
           son = temp->next[i];					//遍历一次全部儿子,每次记为SON
           if(son != NULL){						//存在这个儿子
               if(temp == root) {son->fail = root;}	//temp是根则son失配时必指向根
               else{
                   p = temp->fail;				//读出temp的失配指针
                   while( p ) {					//除了根肯定不为空
                       if(p->next[i]){			//如果temp失配指针结点有一样的儿子
                           son->fail=p->next[i]; //则son指向那个儿子
                           break;
                       }
                       p=p->fail;				//如果没有这个儿子就递归失配!
                   }
                   if(!p)  son->fail=root;	//广搜下失配指针为空的只有根故son指向根
               }
               que.push(son);//把指向这个儿子的指针压入队列
           }
       }
   }
}
void query()
{
    int len, i, cnt = 0;
    len = strlen(str);
    node *p, *temp;
    p = root;
    for( i = 0; i < len; i++)
    {
        int pos = str[i]-'a';
        while( !p->next[pos]&&p!=root )//因为根是没有失配指针的
		p = p->fail;//递归失配
        p = p->next[pos];//访问儿子
        if( !p ) p=root;//没有这个儿子就回到根开始
        temp = p;//保存当前扫到的结点P,用TEMP来计算
        while( temp!=root )
        {
            if(temp->count >= 0)
            {
                cnt += temp->count;
                temp->count = -1;
            }
            else break;
            temp = temp->fail;//当前结点的所有失配点的数量都会被计算,因为本题是求有几个串是模板串的子串
        }
    }
    printf("%d\n",cnt);
}
int main()
{
    int cas,n;						//案例数
    scanf("%d",&cas);
    while(cas--)
    {
        root=new node;
        root->init();
        root->fail=NULL;
        scanf("%d",&n);			//一共有多少个子串
        int i;
        getchar();					//吸收回车符
        for(i=0;i<n;i++)
        {
            gets(str);				//输入N个子串并插入字典树,str全局变量不用传参
            insert();
        }
        getfail();					//计算失配指针
        gets(str);					//输入搜索串
        query();					//询问搜索串出出现,子串的次数
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值