HDU 4787 GRE Words Revenge 分块式在线AC自动机 2013年成都现场赛G题

题目大意:

就是现在有n次操作, 每次操作提供一个字符串或者询问一个字符串中有多少个字串是在之前提供的串中出现过的, 其中还有一个L表示旋转操作就是为了使得这个问题在线操作

每组数据提供的串的总长度不超过10万, 询问的字符串 总长度不超过500万


大致思路:

如果不是在线的话很明显可以用AC自动机来做, 但是由于L的原因强制在线, 而对于每一次插入新串都进行一次AC自动机的建立是不现实的, 于是需要用到一个分块的思想, 创建两个AC自动机, 每次出现新的串需要插入时就将它插入第二个自动机中, 然后当第二个自动机的节点数达到sqrt(10^5)之后就将两个自动机合并到第一个上, 至于合并方法可以先合并两个自动机的Trie数再重新建立自动机, 这个时候将第二个自动机清空, 对于每次查询, 将那个串在两个两个自动机当中都遍历一下就可以了, 为了避免重复我选择了用一个Trie树来记录是够出现重复的提供的串, 这样子均摊下来复杂度为O(nsqrt(n)), n<=10^5, 还是可行的

另外就是合并Trie树的时候刚开始我用dfs合并爆栈了, 开了栈外挂才能过

改成bfs合并就行了


代码如下:

Result  :  Accepted     Memory  :  10156 KB     Time  :  858 ms

/*
 * Author: Gatevin
 * Created Time:  2015/5/1 9:23:42
 * File Name: Rin_Tohsaka.cpp
 */
//#pragma comment(linker, "/STACK:16777216")
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define foreach(e, x) for(__typeof(x.begin()) e = x.begin(); e != x.end(); ++e)
#define SHOW_MEMORY(x) cout<<sizeof(x)/(1024*1024.)<<"MB"<<endl

#define mp make_pair

struct Trie
{
    int next[100010][2];
    bool end[100010];
    int L, root;
    int newnode()
    {
        next[L][0] = next[L][1] = -1;
        end[L++] = 0;
        return L - 1;
    }
    void init()
    {
        L = 0;
        root = newnode();
    }
    bool insert(char *s, int len, int change)
    {
        int now = root;
        for(int i = change; i < len; i++)
        {
            if(next[now][s[i] - '0'] == -1)//之前这里写的next[now][i]居然过样例了=_=
                next[now][s[i] - '0'] = newnode();
            now = next[now][s[i] - '0'];
        }
        for(int i = 0; i < change; i++)
        {
            if(next[now][s[i] - '0'] == -1)
                next[now][s[i] - '0'] = newnode();
            now = next[now][s[i] - '0'];
        }
        if(end[now]) return false;//出现过了
        return end[now] = 1;//新串出现
    }
};
Trie trie;//trie树判重


struct Aho_Corasick_Automation
{
    int next[100010][2], fail[100010];
    int end[100010];
    bool flag[100010][2];//flag[i][j]记录next[i][j]在build中是否从-1被改变
    int last[100010];//记录没有build之前的end
    int L, root;
    int newnode()
    {
        next[L][0] = next[L][1] = -1;
        flag[L][0] = flag[L][1] = 0;
        last[L] = 0;
        end[L++] = 0;
        return L - 1;
    }
    void init()
    {
        L = 0;
        root = newnode();
    }
    void insert(char *s, int len, int change)
    {
        int now = root;
        for(int i = change; i < len; i++)
        {
            if(next[now][s[i] - '0'] == -1)
                next[now][s[i] - '0'] = newnode();
            now = next[now][s[i] - '0'];
        }
        for(int i = 0; i < change; i++)
        {
            if(next[now][s[i] - '0'] == -1)
                next[now][s[i] - '0'] = newnode();
            now = next[now][s[i] - '0'];
        }
        end[now] = 1, last[now] = 1;
    }
    void build()
    {
        fail[root] = root;
        queue <int> Q;
        Q.push(root);
        while(!Q.empty())
        {
            int now = Q.front();
            Q.pop();
            end[now] += end[fail[now]];
            for(int i = 0; i < 2; i++)
                if(next[now][i] == -1)//需要标记原来的Trie树在这里不继续走了
                {
                    next[now][i] = now == root ? root : next[fail[now]][i];
                    flag[now][i] = 1;
                }
                else
                {
                    fail[next[now][i]] = now == root ? root : next[fail[now]][i];
                    Q.push(next[now][i]);
                }
        }
    }
    int query(char *s, int len, int change)
    {
        int now = root;
        int ret = 0;
        for(int i = change; i < len; i++)
        {
            now = next[now][s[i] - '0'];
            ret += end[now];
        }
        for(int i = 0; i < change; i++)
        {
            now = next[now][s[i] - '0'];
            ret += end[now];
        }
        return ret;
    }
};

Aho_Corasick_Automation AC, buf;
int limit;//buf自动机最大规模, 超过这个规模需要合并

queue <pair<int, int> > T;

void bfs()//合并Trie树
{
    while(!T.empty()) T.pop();
    T.push(mp(AC.root, buf.root));
    while(!T.empty())
    {
        int ac_now = T.front().first, buf_now = T.front().second;
        T.pop();
        if(buf.end[buf_now]) AC.end[ac_now] = AC.last[ac_now] = 1;
        for(int i = 0; i < 2; i++)
        {
            if(buf.next[buf_now][i] == -1) continue;
            if(AC.next[ac_now][i] == -1)
                AC.next[ac_now][i] = AC.newnode();
            T.push(mp(AC.next[ac_now][i], buf.next[buf_now][i]));
        }
    }
    return;
}

/* dfs合并两棵树爆栈了
   加上第一行的那个#pragma comment(linker, "/STACK:16777216")交C++就能过了
void dfs(int ac_now, int buf_now)
{
    if(buf.end[buf_now]) AC.end[ac_now] = AC.last[ac_now] = 1;//标记结尾结点
    for(int i = 0; i < 2; i++)
    {
        if(buf.next[buf_now][i] == -1) continue;
        if(AC.next[ac_now][i] == -1)
            AC.next[ac_now][i] = AC.newnode();
        dfs(AC.next[ac_now][i], buf.next[buf_now][i]);
    }
    return;
}
*/

void merge()//合并AC和buf两个自动机至AC, 先合并Trie树然后重建自动机
{
    for(int i = 0; i < AC.L; i++)
    {
        AC.end[i] = AC.last[i];
        for(int j = 0; j < 2; j++)
            if(AC.flag[i][j]) AC.next[i][j] = -1, AC.flag[i][j] = 0;//还原成Trie树
    }
    //dfs(AC.root, buf.root);//将buf的Trie中所有的结点合并到AC的Trie, 爆栈, 需要栈外挂
    bfs();
    AC.build();//重建AC的自动机
    buf.init(), buf.build();
}

void add(char *s, int len, int change)
{
    if(!trie.insert(s, len, change)) return;//去重
    for(int i = 0; i < buf.L; i++)//还原trie树
    {
        buf.end[i] = buf.last[i];
        for(int j = 0; j < 2; j++)
            if(buf.flag[i][j]) buf.next[i][j] = -1, buf.flag[i][j] = 0;
    }
    buf.insert(s, len, change);
    if(buf.L >= limit)//当buf的大小达到了限制就进行合并
        merge();
    else buf.build();
}

lint answer(char *s, int len, lint change)
{
    return AC.query(s, len, change) + buf.query(s, len, change);
}

char s[5000010];

int main()
{
    int T, n;
    scanf("%d", &T);
    limit = (int)sqrt(100000) + 1;
    for(int cas = 1; cas <= T; cas++)
    {
        printf("Case #%d:\n", cas);
        AC.init(), buf.init(), trie.init();
        AC.build(), buf.build();
        scanf("%d", &n);
        getchar();
        int change = 0;
        int tmp_len;
        //SHOW_MEMORY(trie); SHOW_MEMORY(buf), SHOW_MEMORY(AC);
        //SHOW_MEMORY(s);
        while(n--)
        {
            //gets(s);
            scanf("%s", s);
            tmp_len = strlen(s + 1);
            switch(s[0])
            {
                case '+': add(s + 1, tmp_len, change % tmp_len);
                          break;
                case '?': change = answer(s + 1, tmp_len, change % tmp_len);
                          printf("%d\n", change);
                          break;
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值