HDU 4787 GRE Words Revenge AC自动机

【题目大意】

某人学习单词,有2中操作。1:+w,这个人学些了单词w;2:?p,这个人阅读一篇文章p。对于每次阅读,输出其中学习了的单词数,即字符串p中有多少个子串是出现过的。特别地,为了使你的算法是在线的,输入的所有串是加密的,如果前一个答案为L,实际的串是输出的串左移L次的那个串,默认初始答案为L。

【思路】

如果只是一个询问,那么做法很简单,就是很裸的AC自动机。但是不断有1,2操作的时候呢?如果每次都重构AC自动机,因为构造fail指针是需要把字典树bfs一次的,效率太差。这个时候,想到一个分块思想,另 limit = sqrt(10^5),我们构造两个AC自动机,每次把学习的字母加到后一个AC自动机中,如果后者的串总长>=limit,就是后一个AC自动机合到第一个中。

这样的话,查询的效率O(sum(p))。构造AC自动机的效率,显然,合并操作不会超过sqrt(10^5)次,一次合并效率最坏为 10^5 ,而每次后一个AC自动机重构fail指针的效率总和最坏也不会超过n*sqrt(n)。


#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef __int64 LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const int INF=100011122;
const double INFF=1e100;
const double eps=1e-8;
const int mod=1000000007;
const int NN=210;
const int MM=5000010;
/* ****************** */

char ss[MM];
char s1[MM];

const int k_size=2;
const int LEN=100005; //子串最大长度
struct Tire_tree
{
    int fail,ge,id;
    int t_next[k_size];
    int next[k_size];
    void init()
    {
        id=0;
        fail=-1;//我普遍习惯用-1表示空
        memset(next,-1,sizeof(next));
    }
}tire[LEN],tire1[LEN];
int tire_q[LEN]; //用bfs模拟dfs,处理fail指针

void tire_insert(char *str,int l,int root,Tire_tree* tire,int& tire_cnt)
{
    int i,x,p=root;
    for(i=0;i<l;i++)
    {
        x = str[i]-'0'; //把字符变成连续的数字
        if(tire[p].next[x]==-1)
        {
            tire_cnt++;
            tire[tire_cnt].init();
            tire[p].next[x]=tire_cnt;
        }
        p = tire[p].next[x];
    }
    tire[p].id = 1;
}


//处理tire中的fail指针
void init_fail(int root,Tire_tree* tire)
{
    int tail,head;
    int i,fa,p=root;

    head=1;
    tail=0;
    tire[root].fail=root;
    tire[root].ge = 0;

    for(i=0;i<k_size;i++)
    {
        if(tire[p].next[i]!=-1)
        {
            tire_q[++tail] = tire[p].next[i];
            tire[ tire_q[tail] ].fail = root;
            tire[p].t_next[i] = tire[p].next[i];
        }
        else
        {
            tire[p].t_next[i] = root;
           // tire[p].next[i]=root;
        }
    }

    while(head<=tail)
    {
        p = tire_q[head++];
        fa = tire[p].fail;

        tire[p].ge = tire[p].id + tire[fa].ge;

        for(i=0;i<k_size;i++)
        {
            if(tire[p].next[i]!=-1)
            {
                tire_q[++tail] = tire[p].next[i];
                tire[ tire_q[tail] ].fail = tire[fa].t_next[i];
                tire[p].t_next[i] = tire[p].next[i];
            }
            else
            {
               // tire[p].next[i] = tire[fa].next[i];
                tire[p].t_next[i] = tire[fa].t_next[i];
            }
        }
    }
}

void dfs(int p,int cen,int root,int &tire_cnt)
{
    if(tire1[p].id != 0)
    {
        tire_insert(ss,cen,root,tire,tire_cnt);
    }
    int i;
    for(i = 0; i < k_size; i++)
    {
        if(tire1[p].next[i] != -1)
        {
            ss[cen] = i+'0';
            dfs(tire1[p].next[i],cen+1,root,tire_cnt);
        }
    }
}

void merger(int root,int t_root,int &tire_cnt)
{
    dfs(t_root,0,root,tire_cnt);
    init_fail(root,tire);
}

void solve(char *str,int l,int root,Tire_tree *tire,LL &ans)
{
    int i, x, p = root;
    for(i = 0; i < l; i++)
    {
        x = str[i] - '0';
        p = tire[p].t_next[x];
        if(p==-1)
        {
            while(1);
        }
        ans += tire[p].ge;
    }
}

void fun_yi(char *str,int l,char *temp,int x)
{
    int i,j;
    for(i = 0, j = l-x; i < x;i++, j++)
    {
        temp[j] = str[i];
    }
    for(i = x, j = 0; i < l; i++, j++)
    {
        temp[j] = str[i];
    }
}

bool tire_find(char* str,int l,int root,Tire_tree* tire)
{
    int i, x ,p = root;
    for(i = 0; i < l; i++)
    {
        x = str[i] - '0';
        if(tire[p].next[x]==-1)
            return false;
        p = tire[p].next[x];
    }
    return (tire[p].id!=0);
}

int main()
{
    int cas, ee = 0;
    int i, n, l, sum_l;
    int limit = sqrt( 100000.0 );
    int root1,root2;
    int tire_cnt;
    int tire_cnt1;
    LL ans;
    scanf("%d",&cas);
    while(cas--)
    {
        printf("Case #%d:\n",++ee);

        ans = 0;
        sum_l = 0;
        root1 = 0;
        root2 = 0;
        tire[root1].init();
        tire1[root2].init();
        tire_cnt = 0;
        tire_cnt1 = 0;
        init_fail(root1,tire);
        init_fail(root2,tire1);

        scanf("%d",&n);
        for(i = 0; i < n; i++)
        {
            scanf("%s",ss);
            l = strlen(ss);

            fun_yi(ss+1,l-1,s1, (int)(ans%(l-1)) );

            if(ss[0]=='+')
            {
                if( !tire_find(s1,l-1,root1,tire) )
                {
                    tire_insert(s1,l-1,root2,tire1,tire_cnt1);

                    init_fail(root2,tire1);
                }
                sum_l += l-1;
                if(sum_l >= limit)
                {
                    merger(root1,root2,tire_cnt);
                    sum_l = 0;
                    tire_cnt1 = 0;
                    tire1[root2].init();
                    init_fail(root2,tire1);
                }
            }
            else
            {
                ans = 0;
                solve(s1,l-1,root1,tire,ans);
                solve(s1,l-1,root2,tire1,ans);
                printf("%I64d\n",ans);
            }

        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值