Searching the String - ZOJ 3228(ac自动机)

时间:2020-12-10 05:59:39
题目大意:首先给你一下母串,长度不超过10^5,然后有 N(10^5) 次查询,每次查询有两种命令,0或者1,然后加一个子串,询问母串里面有多少个子串,0表示可以重复,1表示不可以重复。
 
分析:发现查询的次数是比较多的,然后可以利用查询的串建立一个trie,然后用母串跑一遍就行了,不过有两种操作比较麻烦一些,不过我们可以保存查询时每个节点的查询结果,然后每个串都对应一个节点,最后输出查询结果即可,这样也可以规避掉重复串的问题。
 
代码如下:
===============================================================================================================
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;

const int MAXN = 6e5+7;
const int MaxSon = 26;

char MumStr[MAXN];
int Num[MAXN], op[MAXN];

struct Ac_Trie
{
    int next[MAXN][MaxSon];
    int Fail[MAXN];
    ///End保存每个单词匹配成功后最后面的位置
    ///如果这个节点是一个单词,Len保存这个单词的长度
    int End[MAXN], Len[MAXN];
    int ans[MAXN][2];///保存两种状态的答案
    int root, cnt;

    int newnode()
    {
        for(int i=0; i<MaxSon; i++)
            next[cnt][i] = -1;
        Len[cnt] = Fail[cnt] = false;
        End[cnt] = -10;
        ans[cnt][0] = ans[cnt][1] = 0;

        return cnt++;
    }
    void InIt()
    {
        cnt = 0;
        root = newnode();
    }
    int Insert(char s[])
    {///返回这个字符串对应的节点编号
        int i, now = root;

        for(i=0; s[i]; i++)
        {
            int k = s[i] - 'a';

            if(next[now][k] == -1)
                next[now][k] = newnode();
            now = next[now][k];
        }

        Len[now] = i;

        return now;
    }
    void GetFail()
    {
        queue<int> Q;
        int i, now = root;

        for(i=0; i<MaxSon; i++)
        {
            if(next[now][i] == -1)
                next[now][i] = root;
            else
            {
                Fail[next[now][i]] = root;
                Q.push(next[now][i]);
            }
        }

        while(Q.size())
        {
            now = Q.front();
            Q.pop();

            for(i=0; i<MaxSon; i++)
            {
                if(next[now][i] == -1)
                    next[now][i] = next[Fail[now]][i];
                else
                {
                    Fail[next[now][i]] = next[Fail[now]][i];
                    Q.push(next[now][i]);
                }
            }
        }
    }
    void Query(char MumStr[])
    {
        int i, now = root, temp;

        for(i=0; MumStr[i]; i++)
        {
            int k = MumStr[i] - 'a';

            now = next[now][k];

            if(!now)continue;

            temp = now;

            while(temp != root)
            {
                if(Len[temp])
                    ans[temp][0]++;
                if(Len[temp] && (i-End[temp] >= Len[temp]) )
                    ans[temp][1]++, End[temp] = i;

                temp = Fail[temp];
            }
        }
    }
};
Ac_Trie ac;
int main()
{
    int i, M, t=1;

    while(scanf("%s", MumStr) != EOF)
    {
        char s[10];
        ac.InIt();

        scanf("%d", &M);

        for(i=1; i<=M; i++)
        {
            scanf("%d%s", &op[i], s);
            Num[i] = ac.Insert(s);
        }

        ac.GetFail();
        ac.Query(MumStr);

        printf("Case %d\n", t++);
        for(i=1; i<=M; i++)
            printf("%d\n", ac.ans[Num[i]][op[i]]);
        printf("\n");
    }

    return 0;
}