zoukankan      html  css  js  c++  java
  • BZOJ4556 Tjoi2016&Heoi2016 字符串【后缀自动机+倍增+线段树合并】

    Description

    佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了
    一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CE
    O,嫁给高富帅,走上人生巅峰。每个问题均有a,b,c,d四个参数,问你子串s[a..b]的所有子串和s[c..d]的最长公
    共前缀的长度的最大值是多少?佳媛姐姐并不擅长做这样的问题,所以她向你求助,你该如何帮助她呢?

    Input

    输入的第一行有两个正整数n,m,分别表示字符串的长度和询问的个数。接下来一行是一个长为n的字符串。接下来
    m行,每行有4个数a,b,c,d,表示询问s[a..b]的所有子串和s[c..d]的最长公共前缀的最大值。1<=n,m<=100,000,
    字符串中仅有小写英文字母,a<=b,c<=d,1<=a,b,c,d<=n

    Output

    对于每一次询问,输出答案。

    Sample Input

    5 5
    aaaaa
    1 1 1 5
    1 5 1 1
    2 3 2 3
    2 4 2 3
    2 3 2 4

    Sample Output

    1
    1
    2
    2
    2


    首先感觉不好处理前缀,就可以把串反转一下变成查询后缀
    然后这样怎么办吗?
    考虑二分最长公共后缀的长度,然后我们就只需要判断在区间([a,b])中有没有这样一个子串
    那么我们就可以找到这个子串所在的节点,判断这个节点的right集合中有没有位置在([a+长度-1,b])之间的
    这样就可以了
    但是我们要怎么找到子串所在的节点呢?首先这个节点一定在parent树上是d对应节点的父亲,所以就可以考虑倍增
    然后就可以(O(logn))得出子串对应的节点
    那么怎么判断right集合中是否存在对应的位置呢?
    考虑用线段树合并就可以了
    然后总复杂度(O(nlog^2n))

    #include<bits/stdc++.h>
    using namespace std;
    #define IL inline
    #define fu(a,b,c) for(int a=b;a<=c;++a)
    #define fd(a,b,c) for(int a=b;a>=c;--a)
    const int CHARSET_SIZE=26;
    const int N=2e5+10;
    const int M=4e6+10;
    IL int read(){
      int ans=0,w=1;char c=getchar();
      while(!isdigit(c)&&c!='-')c=getchar();
      if(c=='-')w=-1,c=getchar();
      while(isdigit(c))ans=(ans<<1)+(ans<<3)+c-'0',c=getchar();
      return ans*w;
    }
    int n,m;char s[N];
    //Segment_Tree
    int tot=0,rt[M],ld[M],rd[M];
    void insert(int &t,int l,int r,int pos){
      t=++tot;
      if(l==r)return;
      int mid=(l+r)>>1;
      if(pos<=mid)insert(ld[t],l,mid,pos);
      else insert(rd[t],mid+1,r,pos);
    }
    int merge(int x,int y){
      if(!x||!y)return x|y;
      int z=++tot;
      ld[z]=merge(ld[x],ld[y]);
      rd[z]=merge(rd[x],rd[y]);
      return z;
    }
    bool query(int t,int l,int r,int L,int R){
      if(!t)return 0;
      if(L<=l&&r<=R)return 1;
      int mid=(l+r)>>1;
      if(R<=mid)return query(ld[t],l,mid,L,R);
      if(L>mid)return query(rd[t],mid+1,r,L,R);
      return query(ld[t],l,mid,L,mid)||query(rd[t],mid+1,r,mid+1,R);
    }
    //Suffix_Automaton
    struct Node{
      int ch[CHARSET_SIZE],prt;
      int maxl,right;
      Node(int maxl=0,int right=0):ch(),prt(0),maxl(maxl),right(right){}
    }t[N];
    int root,last,cur;
    int topo[N],buc[N],pos[N];
    int newnode(int maxl=0,int right=0){t[++cur]=Node(maxl,right);return cur;}
    void init(){cur=0;root=last=newnode();}
    void extend(int c,int id){
      int u=newnode(t[last].maxl+1,1),v=last;
      insert(rt[u],1,n,id);pos[id]=u;
      for(;v&&!t[v].ch[c];v=t[v].prt)t[v].ch[c]=u;
      if(!v){t[u].prt=root;}
      else if(t[v].maxl+1==t[t[v].ch[c]].maxl){
        t[u].prt=t[v].ch[c];
      }else{
        int n=newnode(t[v].maxl+1,0),o=t[v].ch[c];
        memcpy(t[n].ch,t[o].ch,sizeof(t[o].ch));
        t[n].prt=t[o].prt;
        t[o].prt=t[u].prt=n;
        for(;v&&t[v].ch[c]==o;v=t[v].prt)t[v].ch[c]=n;
      }
      last=u;
    }
    void toposort(){
      int maxv=0;
      fu(i,1,cur){
        buc[t[i].maxl]++;
        maxv=max(maxv,t[i].maxl);
      }
      fu(i,1,maxv)buc[i]+=buc[i-1];
      fu(i,1,cur)topo[buc[t[i].maxl]--]=i;
      fu(i,1,maxv)buc[i]=0;
    }
    //ST table
    int F[N][20];
    void get_st_table(){
      fu(i,1,cur)F[i][0]=t[i].prt;
      fu(j,1,18)
        fu(i,1,cur)
          F[i][j]=F[F[i][j-1]][j-1];
    }
    //Main_Solve
    void solve(){
      fd(i,cur,1){
        int u=topo[i],f=F[u][0];
        rt[f]=merge(rt[f],rt[u]);
      }
    }
    int main(){
      n=read();m=read();
      scanf("%s",s+1);
      reverse(s+1,s+n+1);
      init();
      fu(i,1,n)extend(s[i]-'a',i);
      toposort();
      get_st_table();
      solve();
      while(m--){
        int a=n-read()+1,b=n-read()+1,c=n-read()+1,d=n-read()+1;
        swap(a,b);swap(c,d);
        int l=1,r=min(b-a+1,d-c+1),ans=0;
        while(l<=r){
          int mid=(l+r)>>1,p=pos[d];
          fd(i,18,0)if(t[F[p][i]].maxl>=mid)p=F[p][i];
          if(query(rt[p],1,n,a+mid-1,b))ans=mid,l=mid+1;
          else r=mid-1;
        }
        printf("%d
    ",ans);
      }
      return 0;
    }
    
  • 相关阅读:
    转:IOCP在网络应用中常见错误分析
    Ext.Button的禁用
    Excel连接字符串
    从表单为实体对象赋值
    根据指定类型创建数组
    Ext.GridPanel数据显示不正确
    vue 记事本
    杂谈(一)
    推荐《程序设计的 Top 10 做与不做》和《关于编程,鲜为人知的真相》
    (转)黄鸣博客:警惕29岁现象
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9697267.html
Copyright © 2011-2022 走看看