AC自动机

看了这些博客后,自己写的代码.

http://www.cppblog.com/mythit/archive/2009/04/21/80633.html

http://apps.hi.baidu.com/share/detail/16394130

http://apps.hi.baidu.com/share/detail/16394130

http://www.notonlysuccess.com/index.php/aho-corasick-automation/

题目在这:

http://acm.hdu.edu.cn/showproblem.php?pid=2222

代码在这:


#include <cstdio>
#include <cstring>

const int MAX = 10000*50;//最大的节点个数 
const int M = 26;//26个小谢字母 
const int fail = 27;//trie数组中的27号位存下该节点失败后走去哪个节点 
const int isword = 26;//trie数组中的26号位存下该节点是否是一个单词 
const int root = 0;//根的编号为0 
const char a = 'a';
int trie[MAX][M+2];//trie数组 
char str[MAX];//构造用的字符串 
int q[MAX];//队列 
int num;//给每个节点的编号 

void inSert()//插入字符串 
{
    int i = 0;
    int k = root;//从根节点开始 
    
    while (str[i]) {//走到字符串结尾 
        int t = str[i] - a;//转换成数字标号 
        if (trie[k][t] == 0){//假如还没这个节点 
            memset(trie[num],0,sizeof(trie[num]));//申请这个节点 
            trie[k][t] = num;//k进行t匹配到这个新的节点 
            ++num;//编号++ 
        }//假如有了,就不用申请新的节点了. 
        k = trie[k][t];//顺着节点走 
        ++i;//字符串下一个字母 
    }
    
    ++trie[k][isword];//插完这个字符串后,最后一个节点的的isword置真,用于以后统计出现次数    
}

void builtAC()//构建AC自动机 
{
    int s = 0;
    int e = 0;
    
    for (int i = 0; i < M; ++i) {//根节点进行扩展 
        if (trie[root][i]) {//存在这个转移 
            int v = trie[root][i];
            q[e] = v;//进队列 
            ++e; 
            trie[v][fail] = root;//根节点的全部儿子的失败指针指向root 
        }
    }
    
    while (s < e){//BFS
        int t = q[s];//被扩展的节点 
        for (int i = 0; i < M; ++i) {
            if(trie[t][i]) {//扩展出的节点 
                int v = trie[t][i];
                q[e] = v;//进队列 
                ++e;
                int p = trie[t][fail];//被扩展节点的失败指针 
                while (p && trie[p][i] == 0) {//不停的沿着失败指针往root走 
                    p = trie[p][fail];//直到走到有一个能进行转移或者走到root 
                }
                trie[v][fail] = trie[p][i];//扩展出的节点的失败指针 
            }
        }
        ++s;
    }
    
}

int query()
{
    int sum = 0;
    int i = 0;
    int p = root;
    
    while (str[i]) {
        int t = str[i]-a;
        while (trie[p][t] == 0 && p) {//一直找到有个能进行p到t的匹配或者到根 
            p = trie[p][fail];
        }
        p = trie[p][t];//能进行匹配或者到跟 
        int pp = p;//pp用来查找一遍是否存在完整的单词 
        while (pp && trie[pp][isword] != -1) {//没到根或者没被赋值成负过 
            sum += trie[pp][isword];//如果是一个单词就累加 
            trie[pp][isword] = -1;//然后赋值成负的.这样可以加快查找. 
            pp = trie[pp][fail];//pp沿着fail一直往root走 
        }
        ++i;
    }   
     
    return sum;
}

int main()
{   
    int t;
    scanf("%d",&t);
    
    while (t--) {
        int n;
        scanf("%d",&n);
        memset(trie[root],0,sizeof(trie[root]));
        num = 1;
        
        for (int i = 0; i < n; ++i) {
            scanf("%s",str);
            inSert();
        }
        
        builtAC();
        
        scanf("%s",str);
        
        int ans = query();
        
        printf("%d\n",ans);
    }
    
    return 0;
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值