HDU 1277 -- AC自动机的使用(模板补全)

kuangbin巨的专题实在搞不懂了,里面一大堆dp,实在不会写,正好找到了这道题,模板也是终于加上了注释

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1277

题意:就是给你一大~大串数字,再给你几串数字,按照在主串中出现的先后顺序输出序号

思路:直接AC自动机查询不解释,有一个问题是我们要做到按出现的先后顺序输出,这道题与HDU 2896很相似,那道题是查询完之后,依次输出查找成功的(true), 这道题我们可以直接在每次查找成功时,就输出,因为按照Trie图来说, 先找到的也一定在主串中先出现。还有一个问题是,输入的问题,scanf(s)不接受空格, 然后就是我们不能够像以前一样,输入一个子串查询一次了,这次我们要统一查询,在输入时将子串连在一起就好了

AC代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 10;
char str[60005];

int idx(char c){return c - '0';}
struct Trie
{
    int next[10000*65][maxn];
    int fail[10000*65];     //失配指针
    int last[10000*65];      //记录数组
    int root; //根结点指针
    int L;    //总长度
    int newnode()  //获取新结点并初始化
    {
        for(int i = 0; i < maxn; i++) next[L][i] = -1;
        last[L] = -1;
        return L++;
    }
    void init() //初始化
    {
        L = 0;
        root = newnode();
    }
    void insert(char s[],int id)
    {
        int len = strlen(s), now = root;
        for(int i = 0; i < len; i++){
            int c = idx(s[i]);
            if(next[now][c] == -1)  //不存在该结点
                next[now][c] = newnode();
            now = next[now][c];
        }
        last[now]=id;  //记录其id
    }
    void Build()
    {
        queue<int> q;
        fail[root] = root;   //根结点失配指针指向自己
        //根结点的孩子入队,其失配指针指向自己
        for(int i = 0; i < maxn; i++){
            if(next[root][i] == -1)  //不存在该孩子
                next[root][i] = root; //指向自己
            else{
                fail[next[root][i]] = root; //失配指针指向自己
                q.push(next[root][i]);  //孩子入队
            }
        }
        while(!q.empty()){
            int now = q.front();
            q.pop();
            for(int i = 0; i < maxn; i++){
                if(next[now][i] == -1)  //不存在该孩子,指向其父结点失配指针所指向的结点(该结点也有孩子i)
                    next[now][i] = next[fail[now]][i];
                else{
                    fail[next[now][i]] = next[fail[now]][i];
                    q.push(next[now][i]);
                }
            }
        }
    }
    bool used[10001];
    void query(char buf[])
    {
        int len = strlen(buf), now = root;
        memset(used, 0, sizeof(used));
        bool flag = false;
        for(int i = 0; i < len; i++){
            now = next[now][buf[i]-'0'];
            int temp = now;
            while(temp != root)
            {
                if(last[temp] != -1){ //该单词或字符在Trie中出现了
                    used[last[temp]] = 1;
                    if(!flag){
                        printf("Found key:" );
                        flag = true;
                    }
                    printf(" [Key No. %d]", last[temp] );
                }
                temp = fail[temp]; //继续找后缀串
            }
        }
        if(!flag)   puts( "No key can be found !" );
        else    printf("\n");
    }
}trie;

char temp[100];

int main()
{
    int n,m,len,id;
    while (~scanf("%d %d", &m, &n)){
        getchar();
        trie.init();
        memset(str, 0, sizeof(str));
        for (int i = 1; i <= m; i++){
            gets(temp);
            strcat(str, temp);
        }
        getchar();
        for (int i = 1; i <= n; i++){
            scanf("[Key No. %d] ", &id );
            gets(temp);
            trie.insert(temp, id);
        }
        trie.Build();
        trie.query(str);
    }
    return 0;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值