zoukankan      html  css  js  c++  java
  • [后缀数组][线段树] Jzoj P4372 识别子串

    Description

    现在同学们把大多数作业都做完了,但是却被最后一个题给难住了。
    一般地,对于一个字符串S,和S中第k个字符,定义子串T=S(i..j)为一个关于k的识别子串,当且仅当
    1、i<=k<=j。
    2、T在S中只出现一次。
    比如,对于banana的第5个字符,“nana”,“anan”,“anana”,“nan”,“banan”和“banana”都是关于它的识别子串。
    自然,识别子串越短越好(太长了也就失去意义了),现在请你计算出对于一个字符串S,关于S的每一位的最短识别子串分别有多长。
     

    Input

    一行,一个长度为L的字符串S,S只包含小写字母。

    Output

    L行,每行1个整数,第i行的数表示关于S的第i个元素的最短识别子串有多长。
     

    Sample Input

    agoodcookcooksgoodfood

    Sample Output

    1
    2
    3
    3
    2
    2
    3
    3
    2
    2
    3
    3
    2
    1
    2
    3
    3
    2
    1
    2
    3
    4
     

    Data Constraint

    第一个点           L=100
    第二个点           L=1000
    第三个点           L=5000
    第四个点到第十个点 L=100000

    题解

    • 后缀数组可以求出以第i位开头的最短的在原串中只出现过一次的子串

    • 子串的长度是min(height[rank[i]], height[rank[i] + 1) + 1
    • 所以我们枚举每个位置i,找到这个串,然后考虑它的贡献:
    • 对于这个串之内的位置,答案可以用这个串的长度更新;

    • 对于这个串右边的位置,串可以向右“延伸”直到包含该位置(延伸后的串显然也只出现过一次),所以答案可以用(该位置 - i + 1)来更新

    • 可以用线段树维护即可

    • 如果有后缀数组不熟的同学,详见:链接

    代码

      1 #include<cstdio>
      2 #include<iostream>
      3 #include<cstring>
      4 #include<algorithm>
      5 using namespace std;
      6 const int N=500005,inf=1000000000;
      7 int n,s[N],b[N],c[N],d[N],rank[N*2],sa[N],height[N],wz,mn;
      8 char str[N];
      9 struct edge{int v,p;}e[N*4];
     10 void get_SA(int n,int m)
     11 {
     12     for (int i=1;i<=n;i++) b[s[i]]++;
     13     for (int i=1;i<=m;i++) b[i]+=b[i-1];
     14     for (int i=n;i>=1;i--) c[b[s[i]]--]=i;
     15     int x=0,j=1;
     16     for (int i=1;i<=n;i++)
     17     {
     18         if (s[c[i]]!=s[c[i-1]]) x++;
     19         rank[c[i]]=x;
     20     }
     21     while (j<=n)
     22     {
     23         for (int i=1;i<=n;i++) b[i]=0;
     24         for (int i=1;i<=n;i++) b[rank[i+j]]++;        
     25         for (int i=1;i<=n;i++) b[i]+=b[i-1];
     26         for (int i=n;i>=1;i--) c[b[rank[i+j]]--]=i;
     27         for (int i=1;i<=n;i++) b[i]=0;
     28         for (int i=1;i<=n;i++) b[rank[i]]++;
     29         for (int i=1;i<=n;i++) b[i]+=b[i-1];
     30         for (int i=n;i>=1;i--) d[b[rank[c[i]]]--]=c[i];
     31         x=0;
     32         for (int i=1;i<=n;i++)
     33         {
     34             if (rank[d[i]]!=rank[d[i-1]]||rank[d[i]]==rank[d[i-1]]&&rank[d[i]+j]!=rank[d[i-1]+j]) x++;
     35             c[d[i]]=x;
     36         }
     37         for (int i=1;i<=n;i++) rank[i]=c[i];
     38         if (x==n) break; 
     39         j=j*2;
     40     }
     41 }
     42 void get_height(int n)
     43 {
     44     int x=0;
     45     for (int i=1;i<=n;i++) sa[rank[i]]=i;
     46     for (int i=1;i<=n;i++)
     47     {
     48         if (x!=0) x--;
     49         int j=sa[rank[i]-1];
     50         while (i+x<=n&&j+x<=n&&s[i+x]==s[j+x]) x++;
     51         height[rank[i]]=x;
     52     }
     53     height[1]=0;
     54 }
     55 void build(int x,int l,int r)
     56 {
     57     e[x].p=-inf; e[x].v=inf;
     58     if (l==r) return;
     59     int mid=(l+r)/2;
     60     build(x*2,l,mid); build(x*2+1,mid+1,r);
     61 }
     62 void insert1(int id,int l,int r,int x,int y,int z)
     63 {
     64     if (l==x&&r==y) { e[id].v=min(e[id].v,z); return; }
     65     int mid=(l+r)/2;
     66     if (y<=mid) insert1(id*2,l,mid,x,y,z);
     67     else if (x>mid) insert1(id*2+1,mid+1,r,x,y,z);
     68          else insert1(id*2,l,mid,x,mid,z),insert1(id*2+1,mid+1,r,mid+1,y,z);
     69 }
     70 void insert2(int id,int l,int r,int x,int y,int z)
     71 {
     72     if (l==x&&r==y) { e[id].p=max(e[id].p,z); return; }
     73     int mid=(l+r)/2;
     74     if (y<=mid) insert2(id*2,l,mid,x,y,z);
     75     else if (x>mid) insert2(id*2+1,mid+1,r,x,y,z);
     76          else insert2(id*2,l,mid,x,mid,z),insert2(id*2+1,mid+1,r,mid+1,y,z);
     77 }
     78 void query(int id,int l,int r,int x)
     79 {
     80     mn=min(mn,e[id].v); wz=max(wz,e[id].p);
     81     if (l==r) return;
     82     int mid=(l+r)/2;
     83     if (x<=mid) query(id*2,l,mid,x);
     84     else query(id*2+1,mid+1,r,x);
     85 }
     86 int main()
     87 {
     88     scanf("%s",str+1); n=strlen(str+1);
     89     for (int i=1;i<=n;i++) s[i]=str[i]-'a'+1;
     90     get_SA(n,30); 
     91     get_height(n);
     92     build(1,1,n);
     93     for (int i=1;i<=n;i++)
     94     {
     95         int x=sa[i],len=max(height[i],height[i+1]);
     96         if (len==n-x+1) continue;
     97         insert1(1,1,n,x,x+len,len+1);
     98         if (x+len+1<=n) insert2(1,1,n,x+len+1,n,x);
     99     }
    100     for (int i=1;i<=n;i++)
    101     {
    102         mn=inf; wz=-inf; 
    103         query(1,1,n,i);
    104         printf("%d
    ",min(mn,i-wz+1));
    105     }
    106     return 0;
    107 }
  • 相关阅读:
    java中的迭代器的含义
    angular-指令总结
    angular-ng-model
    angular-创建自定义的指令
    angular-$scope和$rootScope
    angular-过滤器
    Git-pull进入vim窗口解决办法
    Math常用方法
    全局变量和window属性上定义的变量
    透明点点的轮播图
  • 原文地址:https://www.cnblogs.com/Comfortable/p/9308501.html
Copyright © 2011-2022 走看看