给定一个长度为\(n(n\le 5\times 10^5)\)的字符串,求它的第\(k\)小字串。有两种模式:

  • \(Type=0\),不同位置的相同字串只算一个
  • \(Type=1\),不同位置相同字串算多个

Sample Input

  1. aabc
  2. 0 3

Sample Output

  1. aab

分析

我们知道,后缀自动机的从小到大遍历可以按顺序得到字符串的所有子串,所以要得到第\(k\)小的字串,只要用类似线段树上二分的做法,记录每一个出去的边后面有多少个即可。记\(val[i]\)为每个点的值,只要求一下\(sum[i]=val[i]+\sum _{trans(i,x)}sum[x]\)。对于两种不同的模式,我们对于\(val\)的设置不同。对于\(type=0\),我们把每个点的\(val\)始终为1,对于\(type=1\),初始时我们把\(val\)设为1,每次新加点的时候,把这个点的整条suffix-link上的点的\(val\)都加一,因为suffix-link代表的是后缀,形成了一个新的字符串后所有后缀的出现次数都增多了,由于一样的子串出现多次算多次,我们要给它算进去。最后dfs一下沿着路查找输出就好啦。很简单的题。

计算\(sum\)的时候,我用的方法是直接dfs一次,而这样会爆栈,所以我手工开栈了。还有一种方法,可以不需要dfs即可解决。我们知道,后缀自动机的\(trans\)组成了一个有向无环图,并且每个点代表的\(len\)与它们的拓扑序相符,而且\(len\)的大小不会超过\(n\),所以我们可以直接对\(len\)进行一次基数排序,相当于完成了拓扑排序,再按照拓扑倒序更新\(sum\)值。这种方法完全不需要递归,就不需要手工开栈了。复杂度同样是\(O(n)\)的。

代码

学一下手工开栈的方法。

更正:bzoj上提交不开栈也能过,因为系统是linux

  1. #include<cstdio>
  2. #include<cctype>
  3. #include<cstring>
  4. #include<cstdlib>
  5. using namespace std;
  6. int read() {
  7. int x=0,f=1;
  8. char c=getchar();
  9. for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
  10. for (;isdigit(c);c=getchar()) x=x*10+c-'0';
  11. return x*f;
  12. }
  13. const int maxn=5e5+1;
  14. const int maxc=26;
  15. char s[maxn];
  16. int sta[maxn<<2],top=0;
  17. bool alr[maxn<<1];
  18. struct SAM {
  19. int t[maxn<<1][maxc],len[maxn<<1],link[maxn<<1],val[maxn<<1],sum[maxn<<1],last,tot;
  20. SAM ():last(1),tot(1) {}
  21. void add(int x,int T) {
  22. int nw=++tot,i;
  23. len[nw]=len[last]+1;
  24. val[nw]=1;
  25. for (i=last;i && !t[i][x];i=link[i]) t[i][x]=nw;
  26. if (i) {
  27. int p=t[i][x];
  28. if (len[p]==len[i]+1) link[nw]=p; else {
  29. int q=++tot;
  30. val[q]=val[p];
  31. len[q]=len[i]+1;
  32. memcpy(t[q],t[p],sizeof t[p]);
  33. for (int j=i;j && t[j][x]==p;j=link[j]) t[j][x]=q;
  34. link[q]=link[p];
  35. link[p]=link[nw]=q;
  36. }
  37. } else link[nw]=1;
  38. if (T) for (int i=link[nw];i;i=link[i]) ++val[i];
  39. last=nw;
  40. }
  41. void dfs(int x) {
  42. sum[x]=val[x];
  43. for (int i=0;i<maxc;++i) if (t[x][i]) {
  44. int v=t[x][i];
  45. if (!alr[v]) {
  46. alr[v]=true;
  47. dfs(v);
  48. }
  49. sum[x]+=sum[v];
  50. }
  51. }
  52. bool run(int x,int k) {
  53. if (!k) return false;
  54. for (int i=0;i<maxc;++i) if (t[x][i]) {
  55. int v=t[x][i];
  56. if (k>sum[v]) k-=sum[v]; else {
  57. putchar(i+'a');
  58. return run(v,k-val[v]);
  59. }
  60. }
  61. return true;
  62. }
  63. } sam;
  64. int main() {
  65. int size=128<<20;
  66. char *p=size+(char*)malloc(size);
  67. __asm__("movl %0, %%esp\n" :: "r"(p));
  68. #ifndef ONLINE_JUDGE
  69. freopen("test.in","r",stdin);
  70. freopen("my.out","w",stdout);
  71. #endif
  72. scanf("%s",s+1);
  73. int n=strlen(s+1);
  74. int T=read(),k=read();
  75. for (int i=1;i<=n;++i) sam.add(s[i]-'a',T);
  76. sam.dfs(1);
  77. int ret=sam.run(1,k);
  78. if (ret) puts("-1"); else puts("");
  79. return 0;
  80. }

bzoj3998-弦论的更多相关文章

  1. BZOJ3998 弦论 【SAM】k小子串

    BZOJ3998 弦论 给一个字符串,问其第\(K\)小字串是什么 两种形式 1.不同起始位置的相同串只算一次 2.不同起始位置的相同串各算一次 首先建\(SAM\) 所有串的数量就是\(SAM\)中 ...

  2. 【BZOJ3998】弦论(后缀自动机)

    [BZOJ3998]弦论(后缀自动机) 题面 BZOJ 题解 这题应该很简单 构建出\(SAM\)后 求出每个点往后还能构建出几个串 按照拓扑序\(dp\)一些就好了 然后就是第\(k\)大,随便搞一 ...

  3. 【BZOJ3998】[TJOI2015]弦论 后缀自动机

    [BZOJ3998][TJOI2015]弦论 Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T ...

  4. [bzoj3998][TJOI2015]弦论_后缀自动机

    弦论 bzoj-3998 TJOI-2015 题目大意:给定一个字符串,求其$k$小子串. 注释:$1\le length \le 5\cdot 10^5$,$1\le k\le 10^9$. 想法: ...

  5. 【BZOJ-3998】弦论 后缀自动机

    3998: [TJOI2015]弦论 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2018  Solved: 662[Submit][Status] ...

  6. 【bzoj3998】 TJOI2015—弦论

    http://www.lydsy.com/JudgeOnline/problem.php?id=3998 (题目链接) 题意 给出一个字符串,求它的字典序第K小的子串是什么,分情况讨论不在同一位置的相 ...

  7. BZOJ3998 TJOI2015弦论(后缀数组+二分答案)

    先看t=1的情况.显然得求出SA(因为我不会SAM).我们一位位地确定答案.设填到了第len位,二分这一位填什么之后,在已经确定的答案所在的范围(SA上的某段区间)内二分,找到最后一个小于当前串的后缀 ...

  8. 【BZOJ3998】弦论 [SAM]

    弦论 Time Limit: 10 Sec  Memory Limit: 256 MB[Submit][Status][Discuss] Description 对于一个给定长度为N的字符串,求它的第 ...

  9. bzoj3998: [TJOI2015]弦论(SAM+dfs)

    3998: [TJOI2015]弦论 题目:传送门 题解: SAM的入门题目(很好的复习了SAM并加强Right集合的使用) 其实对于第K小的字符串直接从root开始一通DFS就好,因为son边是直接 ...

  10. bzoj3998: [TJOI2015]弦论

    SAM小裸题qwq #include <iostream> #include <cstdio> #include <cmath> #include <cstr ...

随机推荐

  1. bos开发日记一

    BOS项目 第1天 项目12天安排: 1-2天:项目概述.搭建开发环境.主页设计.持久层和表现层设计 3-6天:项目业务开发(取派员.区域.分区.定区.业务受理)---整个项目分为基础设置.取派.中转 ...

  2. Error starting mongod. /var/run/mongodb/mongod.pid exists.启动mongodb报错

    linux上安装mongodb,启动时报上面的错,解决如下: 解决方法: 1.删除mongod.pid文件 rm -rf /var/run/mongodb/mongod.pid 2.修改/tmp/mo ...

  3. 【完美解决】Spark-SQL、Hive多 Metastore、多后端、多库

    [完美解决]Spark-SQL.Hive多 Metastore.多后端.多库 [完美解决]Spark-SQL.Hive多 Metastore.多后端.多库 SparkSQL 支持同时连接多种 Meta ...

  4. C# 组装XML传给webserver+XML 返回获取多个xml,根据多个XML 返回dataset类型

    大致流程介绍: 传值给 webserver+XML ,得到webserver+XML多个返回值,组装成dataset形式返回 首先创建所需要的类型 DataSet ds = new DataSet() ...

  5. 用最简单的MVC模式输出内容

    MVC是模型(model)-视图(view)-控制器(controller)的缩写,它的作用是使代码分离,可维护性高.重用性高 编写Model层: <?php class model{ publ ...

  6. java后台接受web前台传递的数组参数

    前台发送:&warning_type[]=1,2 &warning_type=1,2 后台接收:(@RequestParam(value = "param[]") ...

  7. 搭建redis集群的过程中遇到的问题

    1.GCC没有安装或版本不对 报错信息如下 CC adlist.o /bin/sh: cc: command not found make[1]: *** [adlist.o] Error 127 m ...

  8. 【cover-view、cover-image】 覆盖组件说明

    cover-view.cover-image 这两类覆盖组件用于显示在一些特殊组件上方(map.video.canvas.camera.live-player.live-pusher). 这类组件一般 ...

  9. Font Awesome 完美的图标字体

    好久没来,虽说鄙人的人气不咋地,但还是很想念自己这一亩二分田地. 近期用在平台开发中,看着设计师摆开阵势,准备大画图标,想着自己将会很KUBI拼凑css-sprite图片,接着写一大堆 class^= ...

  10. Ubuntu—安装网络调试工具

    https://pan.baidu.com/s/1G6oHXp3SvcN6HMAMqTdqhA 1,在ubuntu的终端下,切换到网络调试工具所在的目录 $ cd 桌面/    #我的放在桌面上 2, ...