BZOJ3277 串 【广义后缀自动机】

Description

字符串是oi界常考的问题。现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中
至少k个字符串的子串(注意包括本身)。

Input

第一行两个整数n,k。
接下来n行每行一个字符串。
n,k,l<=100000

Output

输出一行n个整数,第i个整数表示第i个字符串的答案。

Sample Input

3 1
abc
a
ab

Sample Output

6 1 3


思路

假设不管k的限制,答案应该就是在后缀自动机上面跑到每个点在parent树上到根路径上贡献的和
每个点的贡献就是\(p->maxl-p->prt->maxl\)
然后这个维护直接\(o(n)\)递推就可以了
然后维护每个子串在哪些子串中出现过也很简单
虽然很暴力
直接建出广义后缀自动机之后再把每个串在后缀自动机上跑一遍
跑到的每个节点只需要更新它所有的prt节点就可以了
为了避免算重记录下当前节点上一次是被哪个标记访问的就可以了


#include<bits/stdc++.h>
using namespace std;
#define N 100010
#define fu(a,b,c) for(int a=b;a<=c;++a)
const int CHARSET_SIZE=26;
int n,k;
struct Sam{
  Sam *ch[CHARSET_SIZE],*prt;
  int maxl,val,pre;
  Sam(int maxl=0):maxl(maxl){}
};
struct Suffix_Automaton{
  Sam pool[N<<1],*cur,*root;
  int buc[N];vector<Sam*> topo;
  Suffix_Automaton(){cur=pool;root=new (cur++)Sam;}
  Sam *extend(int c,Sam *last){
    Sam *u=new (cur++)Sam(last->maxl+1),*v=last;
    for(;v&&!v->ch[c];v=v->prt)v->ch[c]=u;
    if(!v)u->prt=root;
    else if(v->ch[c]->maxl==v->maxl+1){
      u->prt=v->ch[c];
    }else{
      Sam *n=new (cur++)Sam(v->maxl+1),*o=v->ch[c];
      copy(o->ch,o->ch+CHARSET_SIZE,n->ch);
      n->prt=o->prt;
      o->prt=u->prt=n;
      for(;v&&v->ch[c]==o;v=v->prt)v->ch[c]=n;
    }
    return u;
  }
  void init(){
    for(Sam *p=pool+1;p!=cur;p++){
      if((signed)p->val<k)p->val=0;
      else p->val=p->maxl-p->prt->maxl;
    }
    int maxv=0;
    for(Sam *p=pool;p!=cur;p++){
      maxv=max(maxv,p->maxl);
      buc[p->maxl]++;
    }
    fu(i,1,maxv)buc[i]+=buc[i-1];
    topo.resize(cur-pool);
    for(Sam *p=pool;p!=cur;p++)topo[--buc[p->maxl]]=p;
    fu(p,1,(signed)topo.size()-1)
      topo[p]->val+=topo[p]->prt->val;
  }
}sam;
struct Node{
  Node *ch[CHARSET_SIZE];
};
struct Trie{
  Node pool[N],*cur,*root;
  Trie(){cur=pool;root=new (cur++)Node;}
  Node *insert(Node *last,int c){
    if(!last->ch[c])last->ch[c]=new (cur++)Node;
    return last->ch[c];
  }
  void insert(char *s,int id){
    int len=strlen(s);
    Node *last=root;
    fu(i,0,len-1)last=insert(last,s[i]-'a');
  }
  #define pi pair<Node*,Sam*>
  void bfs(){
    queue<pi> q;
    q.push(pi(root,sam.root));
    while(!q.empty()){
      pi u=q.front();q.pop();
      fu(i,0,25)if(u.first->ch[i]){
        Sam *tmp=sam.extend(i,u.second);
        q.push(pi(u.first->ch[i],tmp));
      }
    }
  }
}trie;
char s[N];
int bg[N],en[N],c[N];
int main(){
  freopen("input.txt","r",stdin);
  scanf("%d%d",&n,&k);
  int tot=0;
  fu(i,1,n){
    scanf("%s",s);
    int len=strlen(s);
    trie.insert(s,i);
    bg[i]=tot+1;
    fu(j,0,len-1)c[++tot]=s[j]-'a';
    en[i]=tot;
  }
  trie.bfs();
  fu(i,1,n){
    Sam *now=sam.root;
    fu(j,bg[i],en[i]){
      now=now->ch[c[j]];
      Sam *tmp=now;
      for(;tmp!=sam.root&&tmp->pre!=i;tmp=tmp->prt)
        tmp->pre=i,tmp->val++;
    }
  }
  sam.init();
  fu(i,1,n){
    int ans=0;
    Sam *now=sam.root;
    fu(j,bg[i],en[i]){
      for(;now&&!now->ch[c[j]];now=now->prt);
      if(now){
        now=now->ch[c[j]];
        ans+=now->val;
      }else now=sam.root;
    }
    printf("%d ",ans);
  }
  return 0;
}
posted @ 2018-09-27 15:32  Dream_maker_yk  阅读(226)  评论(0编辑  收藏  举报