zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    远程访问linux环境安装图形界面问题解决汇总
    如何通过SecureCRT FTP上传下载文件
    X-Frame-Options 响应头
    Openresty 学习笔记(四)lualocks包管理器安装使用
    PHP7 学习笔记(十六)Yaconf 一个高性能的配置管理扩展
    网络工具(一)frp 供内网穿透服务的工具
    博客园自定义样式参考
    Redis学习笔记(二)解析dump.rdb文件工具之redis-rdb-tools
    Docker 从入门到放弃(四)Docker+Jenkins_自动化持续集成
    PHP7 学习笔记(十五)Repository 模式实现业务逻辑和数据访问的分离
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9713370.html
Copyright © 2011-2022 走看看