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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章