[阈值 二进制分组 && AC自动机]HDU4787. GRE Words Revenge

如果不强制在线,那么可以分治,所以想到可以二进制分组。
但是询问总长度是 5×106 ,乘个log看着虚…

可以只建两个AC自动机,当其中一个AC自动机的节点数超过某个值的时候,就把这个自动机与另一个合并,这样询问的总复杂度就是线性的,插入复杂度是 O(nL)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>

using namespace std;

const int N=500010;

int T,n,cnt,lst,size;
struct NODE{
  NODE *c[2],*fail;
  int val,w;
  NODE(){ val=w=0; c[0]=c[1]=0; }
}a[N],*B,*S;
char s[N*10],t[N*10];

inline void NEW(NODE *&x){
  a[cnt].val=a[cnt].w=0; a[cnt].c[0]=a[cnt].c[1]=0;
  x=a+cnt; cnt++;
}

inline void Add(NODE *&rt,char *a,int len){
  if(!rt) NEW(rt);
  NODE *cur=rt;
  for(int i=1;i<=len;i++){
    if(!cur->c[a[i]-'0']) NEW(cur->c[a[i]-'0']);
    cur=cur->c[a[i]-'0'];
  }
  cur->val=1;
}

NODE *Merge(NODE *&x,NODE *y){
  if(!x || !y) return !x?y:x;
  x->val|=y->val;
  x->c[0]=Merge(x->c[0],y->c[0]);
  x->c[1]=Merge(x->c[1],y->c[1]);
  return x;
}

NODE *Q[N];
int l,r;

inline void Build(NODE *x){
  l=r=1; Q[1]=x;
  x->fail=0; x->w=x->val;
  while(l<=r){
    NODE *u=Q[l++];
    for(int i=0;i<2;i++){
      NODE *v=u->c[i];
      if(!v) continue;
      v->w=v->val;
      if(u==x) v->fail=0;
      else{
    NODE *p=u->fail;
    while(p && !p->c[i]) p=p->fail;
    if(!p) p=x;
    v->fail=p->c[i];
    if(v->fail) v->w+=v->fail->w;
      }
      Q[++r]=v;
    }
  }
}

inline int Query(NODE *rt,char *a,int len){
  if(!rt) return 0;
  int ret=0; NODE *cur=rt;
  for(int i=1;i<=len;i++){
    while(cur && !cur->c[a[i]-'0']) cur=cur->fail;
    if(!cur) cur=rt;
    if(cur->c[a[i]-'0']) cur=cur->c[a[i]-'0'];
    ret+=cur->w;
  }
  return ret;
}

inline bool find(NODE *rt,char *a,int len){
  if(!rt) return false;
  NODE *cur=rt;
  for(int i=1;i<=len;i++){
    if(!cur->c[a[i]-'0']) return false;
    cur=cur->c[a[i]-'0'];
  }
  return cur->val;
}

int main(){
  scanf("%d",&T); int CASE=0;
  while(T--){
    printf("Case #%d:\n",++CASE);
    scanf("%d",&n);
    B=S=0; lst=size=0; int BK=820;
    for(int i=1;i<=n;i++){
      scanf("%s",s); int len=strlen(s+1);
      for(int j=1;j<=len;j++) t[j]=s[(j+lst-1)%len+1];
      if(*s=='+'){
    if(find(B,t,len)) continue;
    Add(S,t,len),size+=len;
    if(size>=BK){
      B=Merge(B,S); Build(B);
      S=0; size=0; 
    }
    else Build(S);
      }
      else{
    printf("%d\n",lst=Query(S,t,len)+Query(B,t,len));
      }
    }
  }
  return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值