HDU4787 GRE Words Revenge(AC自动机 分块 合并)

题目

Source

http://acm.hdu.edu.cn/showproblem.php?pid=4787

Description

  Now Coach Pang is preparing for the Graduate Record Examinations as George did in 2011. At each day, Coach Pang can:
  "+w": learn a word w
  "?p": read a paragraph p, and count the number of learnt words. Formally speaking, count the number of substrings of p which is a learnt words.
  Given the records of N days, help Coach Pang to find the count. For convenience, the characters occured in the words and paragraphs are only ‘0‘ and ‘1‘.

Input

  The first line of the input file contains an integer T, which denotes the number of test cases. T test cases follow.
  The first line of each test case contains an integer N (1 <= N <= 105), which is the number of days. Each of the following N lines contains either "+w" or "?p". Both p and w are 01-string in this problem.
  Note that the input file has been encrypted. For each string occured, let L be the result of last "?" operation. The string given to you has been shifted L times (the shifted version of string s1s2 ... sk is sks1s2 ... sk-1). You should decrypt the string to the original one before you process it. Note that L equals to 0 at the beginning of each test case.
  The test data guarantees that for each test case, total length of the words does not exceed 105 and total length of the paragraphs does not exceed 5 * 106.

Output

  For each test case, first output a line "Case #x:", where x is the case number (starting from 1).
  And for each "?" operation, output a line containing the result.

Sample Input

2
3
+01
+01
?01001
3
+01
?010
?011

Sample Output

Case #1:
2
Case #2:
1
0

分析

题目大概说有依次进行N个操作,每个操作可以是学习一个单词,或者读一个段落并求出段落里有多少个子串是已经学习的单词。

建立两个AC自动机,一个大的,一个小的。每次更新插入到小的自动机并重构,小的自动机结点数有限制,一旦超过限制就将其合并到大的,然后大的重构,小的清空。。如此就OK了。。

这么做的时间复杂度——

  • 不妨设小的自动机大小限制为$\sqrt L$,$L$为插入的模式串总长,于是最多插入$L$次,每次重构fail时间复杂度可以做到线性的即$O(\sqrt L)$,这样小的自动机这儿总时间复杂度是$O(L\sqrt L)$;
  • 对于大的来说,最多的合并次数为$\frac L{\sqrt L}$即$\sqrt L$,每次合并时间复杂度$O(\sqrt L)$,每次重构$O(L)$,那么总的时间复杂度是$O(L\sqrt L)$。
  • 而查询,就是在两个AC自动机上跑一遍主串即可,也是可以做到线性的,即$O(L+\sum |主串|)$。

有点神奇。。

代码

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
#define MAXN 100100

struct AC_auto{
    int ch[MAXN][2],fail[MAXN],tn;
    bool flag[MAXN];
    void init(){
        for(int i=0; i<=tn; ++i){
            ch[i][0]=ch[i][1]=flag[i]=0;
        }
        tn=0;
    }
    void insert(char *s){
        int x=0;
        for(int i=0; s[i]; ++i){
            int y=s[i]-‘0‘;
            if(ch[x][y]==0) ch[x][y]=++tn;
            x=ch[x][y];
        }
        flag[x]=1;
    }
    void getfail(){
        for(int i=0; i<=tn; ++i) fail[i]=0;
        queue<int> que;
        for(int i=0; i<2; ++i){
            if(ch[0][i]) que.push(ch[0][i]);
        }
        while(!que.empty()){
            int x=que.front(); que.pop();
            for(int i=0; i<2; ++i){
                if(ch[x][i]==0) continue;
                que.push(ch[x][i]);
                int tmp=fail[x];
                while(tmp && ch[tmp][i]==0){
                    tmp=fail[tmp];
                }
                fail[ch[x][i]]=ch[tmp][i];
            }
        }
    }
    int match(char *s){
        int x=0,ret=0;
        for(int i=0; s[i]; ++i){
            int y=s[i]-‘0‘;
            while(x && ch[x][y]==0) x=fail[x];
            x=ch[x][y];
            int tmp=x;
            while(tmp){
                if(flag[tmp]) ++ret;
                tmp=fail[tmp];
            }
        }
        return ret;
    }
    bool query(char *s){
        int x=0;
        for(int i=0; s[i]; ++i){
            int y=s[i]-‘0‘;
            if(ch[x][y]==0) return 0;
            x=ch[x][y];
        }
        return flag[x];
    }
}ac,buf;

void dfs(int u,int v){
    for(int i=0; i<2; ++i){
        if(buf.ch[v][i]==0) continue;
        if(ac.ch[u][i]==0){
            ac.ch[u][i]=++ac.tn;
            ac.ch[ac.tn][0]=ac.ch[ac.tn][1]=0;
            ac.flag[ac.tn]=0;
        }
        if(buf.flag[buf.ch[v][i]]) ac.flag[ac.ch[u][i]]=1;
        dfs(ac.ch[u][i],buf.ch[v][i]);
    }
}
void join(){
    dfs(0,0);
    buf.init();
    ac.getfail();
}

char str[5111111],s[5111111];
int main(){
    int t;
    scanf("%d",&t);
    for(int cse=1; cse<=t; ++cse){
        printf("Case #%d:\n",cse);
        ac.init();
        buf.init();
        int n;
        scanf("%d",&n);
        int lastans=0;
        char op;
        while(n--){
            scanf(" %c",&op);
            scanf("%s",str);
            int len=strlen(str);
            for(int i=0; i<len; ++i){
                s[i]=str[(i+lastans)%len];
            }
            s[len]=0;
            if(op==‘+‘){
                if(ac.query(s) || buf.query(s)) continue;
                buf.insert(s);
                buf.getfail();
                if(buf.tn>2000) join();
            }else{
                lastans=ac.match(s)+buf.match(s);
                printf("%d\n",lastans);
            }
        }
    }
    return 0;
}
时间: 2024-11-06 23:12:11

HDU4787 GRE Words Revenge(AC自动机 分块 合并)的相关文章

[HDU 4787] GRE Words Revenge (AC自动机)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4787 题目大意: 给你若干个单词,查询一篇文章里出现的单词数.. 就是被我水过去的...暴力重建AC自动机- -然后暴力查找.. 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 #include <map> 6 #include

HDU 3341 Lost&#39;s revenge AC自动机+dp

Lost's revenge Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 3757    Accepted Submission(s): 1020 Problem Description Lost and AekdyCoin are friends. They always play "number game"(A bor

Hdu 3341 Lost&#39;s revenge (ac自动机+dp+hash)

题目大意: 给出很多个DNA串,每一个串的价值为1,最后给出一个长串,要你重新排列最后的串使之它所有的子串的权值和最大. 思路分析: 最先容易想到的思路就是搜!管她3721..直接一个字符一个字符的码,然后在AC自动机上判断最后的权值.TLE哟. 然后发现搜过不去,那就dp咯.再容易想到的就是dp[i][a][b][c][d] 表示此时遍历AC自动机的节点在i,然后构成了a个A,b个G,c个C,d个T的权值. 再一看内存,500*40*40*40*40...然后...就没有然后了 再想,因为它说

hdu 3341 Lost&#39;s revenge(AC自动机+变进制状压DP)

题目链接:hdu 3341 Lost's revenge 题目大意:给定一些需要匹配的串,然后在给定一个目标串,现在可以通过交换目标串中任意两个位置的字符,要求最 后生成的串匹配尽量多的匹配串,可以重复匹配. 解题思路:这题很明显是AC自动机+DP,但是dp的状态需要开40?40?40?40(记录每种字符的个数),空间承受 不了,但是其实因为目标串的长度有限,为40:所以状态更本不需要那么多,最多只有10?10?10?10,但是通过 40进制的hash转换肯定是不行,可以根据目标串中4种字符的个

HDU 3341 Lost&#39;s revenge AC自动机+ 状态压缩DP

题意:这个题目和HDU2457有点类似,都是AC自动机上的状态dp,题意就是给你只含有'A','T','C','G',四个字符的子串和文本串,问你文本串如何排列才可以使得文本串中包含有更多的模式串 解题思路:我们知道了 有 num[0] 个 'A', num[1] 个 ‘T’, num[2] 个 ‘C’,num[3] 个‘G’, 我们的可以知道暴力的思路就是把所有的文本串都枚举出来然后一一匹配.我们膜拜了一下春哥以后,就可以有以下思路:  把一个串的信息压缩一下,把具有同样个数字符的串看成是同一

HDOJ 题目4787 GRE Words Revenge(在线ac自动机,离线也可做)

GRE Words Revenge Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 327680/327680 K (Java/Others) Total Submission(s): 1570    Accepted Submission(s): 352 Problem Description Now Coach Pang is preparing for the Graduate Record Examinations as

AC自动机

AC自动机 直接学AC自动机比较难理解,强烈建议先学完KMP和字典树并进行一定的练习后,对于失配指针和字典树构造有一定理解后再来学AC自动机的内容.有关AC自动机的详细介绍可见刘汝佳的<算法竞赛入门经典训练指南>P214. 给你一个字典(包含n个不重复的单词),然后给你一串连续的字符串文本(长为len),问你该文本里面的哪些位置正好出现了字典中的某一个或某几个单词?输出这些位置以及出现的单词. 这个问题可以用n个单词的n次KMP算法来做(效率为O(n*len*单词平均长度)),也可以用1个字典

hdu 4787 GRE Words Revenge 在线AC自动机

hdu 4787 GRE Words Revenge Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 327680/327680 K (Java/Others)Total Submission(s): 2505    Accepted Submission(s): 614 Problem Description Now Coach Pang is preparing for the Graduate Record Examina

hdu4787 AC自动机加分块

这题说的是 有n次操作 +w 表示读入一个字符串,?p 询问这个字符串的子串在那些模板串中有多少个, http://blog.csdn.net/qq574857122/article/details/16826631 就是说先存一部分的字符串,因为每次都要进行重新 建立这个失配指针,也就是说让适当的单词进行失配 指针重建 会达到高效,两个ac自动机,选取sqrt(100000)的时候达到相对优一点,这样我们 当第一棵树超过了 800的时候我们就将第一棵树的东西存入第二棵树这样我们可以相对减少对失