• BZOJ3998:[TJOI2015]弦论(SAM)


    Description

    对于一个给定长度为N的字符串,求它的第K小子串是什么。

    Input

    第一行是一个仅由小写英文字母构成的字符串S

    第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个。T=1则表示不同位置的相同子串算作多个。K的意义如题所述。

    Output

    输出仅一行,为一个数字串,为第K小的子串。如果子串数目不足K个,则输出-1

    Sample Input

    aabc
    0 3

    Sample Output

    aab

    HINT

    N<=5*10^5

    T<2
    K<=10^9

    Solution

    对于$t=0$的情况,我们将$right$集合赋值为$1$,否则就赋成正常的$right$集合大小。

    因为$SAM$是一个$DAG$,所以可以对$right$集合求一个后缀和$sum$,然后用类似于平衡树查找第$k$大的方式找一下就好了。具体可以看代码,还是很好懂的……

    Code

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #define N (1000009)
     5 using namespace std;
     6 
     7 int n,t,k,flag;
     8 char s[N];
     9 
    10 struct SAM
    11 {
    12     int p,q,np,nq,last,cnt;
    13     int fa[N],son[N][26],step[N],right[N];
    14     int d[N],sum[N];
    15     int wt[N],od[N];
    16     SAM(){last=cnt=1;}
    17     
    18     void Insert(int x)
    19     {
    20         p=last; np=last=++cnt; step[np]=step[p]+1; right[np]=1;
    21         while (!son[p][x] && p) son[p][x]=np, p=fa[p];
    22         if (!p) fa[np]=1;
    23         else
    24         {
    25             q=son[p][x];
    26             if (step[p]+1==step[q]) fa[np]=q;
    27             else
    28             {
    29                 nq=++cnt; step[nq]=step[p]+1;
    30                 memcpy(son[nq],son[q],sizeof(son[q]));
    31                 fa[nq]=fa[q]; fa[q]=fa[np]=nq;
    32                 while (son[p][x]==q) son[p][x]=nq, p=fa[p];
    33             }
    34         }
    35     }
    36     void Calc()
    37     {
    38         for (int i=1; i<=cnt; ++i) wt[step[i]]++;
    39         for (int i=1; i<=n; ++i) wt[i]+=wt[i-1];
    40         for (int i=cnt; i>=1; --i) od[wt[step[i]]--]=i;
    41         for (int i=cnt; i>=1; --i)    
    42             if (t) right[fa[od[i]]]+=right[od[i]];
    43             else right[od[i]]=1;
    44         right[1]=0;
    45         for (int i=cnt; i>=1; --i)
    46         {
    47             sum[od[i]]=right[od[i]];
    48             for (int j=0; j<26; ++j)
    49                 sum[od[i]]+=sum[son[od[i]][j]];
    50         }
    51     }
    52     void Query(int x,int k)
    53     {
    54         if (k<=right[x]) return;
    55         k-=right[x];
    56         for (int i=0; i<26; ++i)
    57         {
    58             if (!son[x][i]) continue;
    59             if (k<=sum[son[x][i]])
    60             {
    61                 flag=1;
    62                 printf("%c",'a'+i);
    63                 Query(son[x][i],k);
    64                 return;
    65             }
    66             k-=sum[son[x][i]];
    67         }
    68     }
    69 }SAM;
    70 
    71 int main()
    72 {
    73     scanf("%s%d%d",s,&t,&k);
    74     n=strlen(s);
    75     for (int i=0; i<n; ++i)
    76         SAM.Insert(s[i]-'a');
    77     SAM.Calc(); SAM.Query(1,k);
    78     if (!flag) puts("-1");
    79 }
  • 相关阅读:
    手工测试
    测试理论
    MySQL常用语法
    Linux设置静态ip
    设计模式
    Shiro
    TreeSet和TreeMap
    UDP和反射
    Linux归纳
    Spring+SpringMVC+Mybatis整合
  • 原文地址:https://www.cnblogs.com/refun/p/10419569.html
Copyright © 2020-2023  润新知