HDU 4787 GRE Words Revenge [二进制分组][AC自动机]

题意

维护支持插入和询问的AC自动机,强制在线

题解

二进制分组(可以百度xhr的答辩论文)

对于每一个分组单独建立AC自动机,合并分组时暴力重构

还有一个细节是对于重串,要直接踢掉,可以用set或者hash判一下

因为一样的串在不同的AC自动机里出现还是当一次算

代码又慢又长…大概是string的读入拖慢速度,用时倒数…

//按我的打法原本AC自动机的初始状态各个用来模拟指针的变量都是指向根

//所以这里在数组分配每一个AC自动机时需要初始化到指向根

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<set>
#include<iostream>
#define N 200005
#define M 5000005
#define cl(x) memset(x,0,sizeof(x))
using namespace std;

struct node{
    int fail,lk[2];
    bool mark;
    inline void clear(){
        lk[0]=lk[1]=fail=mark=0;
    }
}ac[N];

int n,cnt,top,size,root[30],L[30],R[30];
string s,p[N];
set<string>lyf;

inline void insert(int x,int rt){
    int len=p[x].length()-1,nw=rt;
    for(int i=1,t;i<=len;++i){
        t=p[x][i]-'0';
        if(!ac[nw].lk[t]) ac[nw].lk[t]=++size;
        nw=ac[nw].lk[t];
    }
    ac[nw].mark=1;
}

inline void build(int rt){
    queue<int>q;
    for(int i=0;i<=1;++i)
        if(ac[rt].lk[i]!=rt){
            ac[ac[rt].lk[i]].fail=rt;
            q.push(ac[rt].lk[i]);
        }
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=0;i<=1;++i)
            if(ac[x].lk[i]!=rt){
                ac[ac[x].lk[i]].fail=ac[ac[x].fail].lk[i];
                q.push(ac[x].lk[i]);
            }
            else ac[x].lk[i]=ac[ac[x].fail].lk[i];
    }
    return;
}

inline void add(){
    p[++cnt]=s;
    L[++top]=cnt;
    while(top>1&&cnt-L[top]+1==L[top]-L[top-1]) --top;
    for(int i=R[top-1]+1;i<=size;++i) ac[i].clear();
    root[top]=R[top-1]+1,size=root[top];
    for(int i=L[top];i<=cnt;++i) insert(i,root[top]);
    for(int i=root[top];i<=size;++i){
        if(!ac[i].lk[0]) ac[i].lk[0]=root[top];
        if(!ac[i].lk[1]) ac[i].lk[1]=root[top];
//      ac[i].fail=root[top];
    }
    build(root[top]);
    R[top]=size;
}

string tmp;

void deal(int x){
    int len=s.length()-1;
    x%=len;
    tmp="";
    for(int i=x+1;i<=len;++i) tmp+=s[i];
//  cout<<tmp<<endl;
    for(int i=1;i<=x;++i) tmp+=s[i];
    s=s[0]+tmp;
//  cout<<s<<endl;
}

int ask(int rt){
    int len=s.length()-1,res=0,nw=rt;
    for(int i=1,nxt;i<=len;++i){
        nw=nxt=ac[nw].lk[s[i]-'0'];
        while(nxt!=rt){
            res+=ac[nxt].mark;
            nxt=ac[nxt].fail;
        }
    }
    return res;
}

int query(){
    int res=0;
    for(int i=1;i<=top;++i) res+=ask(root[i]);
    return res;
}

int main(){
    int test;
    scanf("%d",&test);
    for(int t=1;t<=test;++t){
        for(int i=1;i<=size;++i) ac[i].clear();
        cl(root),cl(L),cl(R);
        lyf.clear();
        R[0]=-1;
        cnt=top=0;
        size=0;
        scanf("%d",&n);
        int ans=0;
        printf("Case #%d:\n",t);
        for(int i=1;i<=n;++i){
            cin>>s;
            deal(ans);
            if(s[0]=='+'){
                int pos=lyf.count(s);
                if(pos==1) continue;
                lyf.insert(s);
                add();
            }
            else printf("%d\n",ans=query());
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值