【二进制分组+AC自动机】HDU4787[GRE Words Revenge]题解

来源:互联网 发布:网络摄像机厂家 编辑:程序博客网 时间:2024/05/17 10:28

题目概述

给出 n 个操作,操作有两种:1.加入一个 01 串。2.询问一个 01 串中子串是学过单词的个数。强制在线

解题报告

如果是插入完再询问,就是AC自动机裸题。如果不强制在线,可以按照时间分治。

然而都不满足啊,只能另想办法了。又一个神奇的暴力,二进制分组就派上用场了。

每次插入都必须重建AC自动机,但是这是无法承受的。我们将插入分组,如果这一组与上一组的个数相同,那么就将两者合并,并重建AC自动机。

差不多是这样:

11 1 -> 22 12 1 1 -> 2 2 -> 4

因为每个串所在组的大小每次 ×2 ,所以每个串只会被插入 log2n 次,和启发式合并一样。

最后在每个组(至多 log2n 个)中都暴力询问一遍答案就行了。没注意到相同串,调到吐血QAQ。

示例程序

因为实在不怎么相信string的速度QAQ,所以写了很奇怪的字符数组,并且用了set+hash判重。

#include<cstdio>#include<cctype>#include<cstring>#include<set>using namespace std;typedef long long LL;typedef unsigned long long ULL;const int maxn=1e5,maxl=5e6,Ba=3214567;int te,Q;char s[maxl+5];LL lstans;int n,pos,str[maxn+5];char tem[maxl+maxn+5];int len,son[maxl+5][2],fai[maxl+5],num[maxn+5];int blk,si[maxn+5],ro[maxn+5],que[maxn+5];set<ULL> f;#define Eoln(x) ((x)==10||(x)==13||(x)==EOF)inline char readc(){    static char buf[100000],*l=buf,*r=buf;    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);    if (l==r) return EOF;return *l++;}inline int readi(int &x){    int tot=0,f=1;char ch=readc(),lst='+';    while (!isdigit(ch)) {if (ch==EOF) return EOF;lst=ch;ch=readc();}    if (lst=='-') f=-f;    while (isdigit(ch)) tot=(tot<<3)+(tot<<1)+ch-48,ch=readc();    return x=tot*f,Eoln(ch);}inline char getfst() {char ch=readc();while (ch!='+'&&ch!='?') ch=readc();return ch;}inline int reads(char *s){    int len=0;char ch=readc();if (ch==EOF) return EOF;    s[++len]=ch;while (isdigit(s[len])) s[++len]=readc();    return s[len--]=0,len;}inline void Build(int ro){    int Head=0,Tail=0,u;    for (int i=0;i<2;i++)        if (u=son[ro][i]) que[++Tail]=u,fai[u]=ro; else son[ro][i]=ro;    while (Head!=Tail)    {        int x=que[++Head];num[x]+=num[fai[x]];        for (int i=0;i<2;i++)            if (u=son[x][i]) que[++Tail]=u,fai[u]=son[fai[x]][i]; else            son[x][i]=son[fai[x]][i];    }}#define S(i) (tem+str[i])inline char* Add(char *s,char f){    int len=strlen(s+1);str[++n]=pos;S(n)[len+1]=0;    for (int i=1;i<=len;i++) S(n)[i]=s[(i+lstans-1)%len+1];    if (f=='+') return pos+=len+1,S(n); else return S(n--);}#define newnode (len++,son[len][0]=son[len][1]=fai[len]=num[len]=0,len)inline void Insert(char *s){    ULL Ha=0;for (int i=1;s[i];i++) Ha=Ha*Ba+s[i];if (f.count(Ha)) {n--;return;}    for (si[++blk]=1;blk>1&&si[blk]==si[blk-1];len=ro[--blk]-1) si[blk-1]+=si[blk];    ro[blk]=newnode;f.insert(Ha);    for (int i=n-si[blk]+1;i<=n;i++)    {        int p=ro[blk];        for (int j=1;S(i)[j];j++) {int &u=son[p][S(i)[j]-'0'];if (!u) u=newnode;p=u;}        num[p]=1;    }    Build(ro[blk]);}inline LL Ask(char *s){    lstans=0;    for (int i=1;i<=blk;i++)    for (int j=1,p=ro[i];s[j];j++)        p=son[p][s[j]-'0'],lstans+=num[p];    return lstans;}int main(){    freopen("program.in","r",stdin);    freopen("program.out","w",stdout);    for (int i=(readi(te),1);i<=te;i++,lstans=0,f.clear())    {        printf("Case #%d:\n",i);n=0;pos=0;blk=0;len=0;        for (readi(Q);Q;Q--)        {            char td=getfst();reads(s);            if (td=='+') Insert(Add(s,td)); else            printf("%lld\n",Ask(Add(s,td)));        }    }    return 0;}
阅读全文
0 0