• 识别子串 (string)——后缀自动机+线段树


    题目

    【题目描述】

    一般地,对于一个字符串 S,和 S 中第 $ i $ 个字符 x,定义子串 $ T=S(i.j) $ 为一个关于 x 的识别子申,当且仅当:
      1.$ i leq x leq j $
      2.T 在 S 巾只出现一次
    比如,对于 banana 的第 $ 5 $ 个字符,“nana”, “anan”,“anana”, “nan”,“banan” 和“banana”都是关于它的识别子串。
    说你写一个程序,计算出对对于一个字符串 S,关于 S 的每一位的最短识别子串的长度。

    【输入格式】

    一行,一个由小写字母组成的字符串 S, 长度不超过 $ 10^5 $

    【输出格式】

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

    【样例输入】

    agoodcookcooksgoodfood

    【样例输出】

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

    【数据范围与提示】

    对于 20% 的数据 L<=200

    对于 40% 的数据 L<=5000

    对于 60% 的数据 L<=20000

    对于 100% 的数据 L<=100000

    题解

    可以发现,对于每一个唯一识别的子串,都可以更新一段答案

    可以发现,在后缀自动机上的每一个叶子节点 $ x $,都是一段唯一识别的子串

    考虑更新,记 $ l=len(x)-len(fa(x)),r=len(x) $,那么对于 $ [1,l-1] $ 的贡献为 $ r-i+1 $,对于 $ [l,r] $ 的贡献为 $ r-l+1 $

    可以把 $r-i+1$ 的 $ i $ 提出,转化为 $query(i)-i$,开两棵线段树分别统计即可

    代码

     1 #include<bits/stdc++.h>
     2 #define LL long long
     3 #define _(d) while(d(isdigit(ch=getchar())))
     4 using namespace std;
     5 int R(){
     6     int x;bool f=1;char ch;_(!)if(ch=='-')f=0;x=ch^48;
     7     _()x=(x<<3)+(x<<1)+(ch^48);return f?x:-x;}
     8 const int N=1e6+5;
     9 int n,las=1,cnt=1,Rt=1;
    10 char s[N];bool f[N];
    11 struct node{int ch[30],fa,len;}tr[N];
    12 void extend(int c){
    13     int p=las,np=las=++cnt,q,nq;
    14     tr[np].len=tr[p].len+1;
    15     while(!tr[p].ch[c]&&p)
    16         tr[p].ch[c]=np,p=tr[p].fa;
    17     if(!p)return void(tr[np].fa=Rt);
    18     if(tr[p].len+1==tr[q=tr[p].ch[c]].len)return void(tr[np].fa=q);
    19     tr[nq=++cnt].len=tr[p].len+1;
    20     memcpy(tr[nq].ch,tr[q].ch,sizeof tr[q].ch);
    21     tr[nq].fa=tr[q].fa,tr[np].fa=tr[q].fa=nq;
    22     while(p&&tr[p].ch[c]==q)
    23         tr[p].ch[c]=nq,p=tr[p].fa;
    24     return;
    25 }
    26 class seg{
    27 private:
    28     #define Ls rt<<1
    29     #define Rs rt<<1|1
    30 public:
    31     int tr[N];
    32     seg(){memset(tr,0x3f,sizeof tr);}
    33     void update(int rt,int l,int r,int ql,int qr,int v){
    34         if(ql>qr)return;
    35         if(ql<=l&&qr>=r)return void(tr[rt]=min(tr[rt],v));
    36         int mid=l+r>>1;
    37         if(ql<=mid)update(Ls,l,mid,ql,qr,v);
    38         if(qr>mid)update(Rs,mid+1,r,ql,qr,v);
    39         return;
    40     }
    41     int query(int rt,int l,int r,int k){
    42         if(l==r)return tr[rt];
    43         int mid=l+r>>1;
    44         if(k<=mid)return min(tr[rt],query(Ls,l,mid,k));
    45         else return min(tr[rt],query(Rs,mid+1,r,k));
    46     }
    47 }T1,T2;
    48 int main(){
    49     scanf("%s",s+1),n=strlen(s+1);
    50     for(int i=1;i<=n;i++)extend(s[i]-'a');
    51     for(int i=1;i<=cnt;i++)f[tr[i].fa]=1;
    52     for(int i=1;i<=cnt;i++)
    53         if(!f[i]){
    54             int x=tr[i].len-tr[tr[i].fa].len,y=tr[i].len;
    55             T1.update(1,1,n,1,x-1,y+1),T2.update(1,1,n,x,y,tr[tr[i].fa].len+1);
    56         }
    57     for(int i=1;i<=n;i++)
    58         printf("%d
    ",min(T1.query(1,1,n,i)-i,T2.query(1,1,n,i)));
    59     return 0;
    60 }
    View Code
  • 相关阅读:
    洛谷P2265 路边的水沟
    洛谷P2015 二叉苹果树
    bzoj2431 || 洛谷P1521 求逆序对
    Python ImportError: cannot import name ABC
    Python ImportError: cannot import name ABC
    [USACO09FEB]股票市场Stock Market
    Python NameError:name ‘xrange’ is not defined
    maven的核心概念——依赖
    maven的核心概念——坐标
    maven的核心概念——POM
  • 原文地址:https://www.cnblogs.com/chmwt/p/10661953.html
Copyright © 2020-2023  润新知