BZOJ1396 识别子串

给定一个串\(s\),对于串中的每个位置,输出经过这个位置且只在\(s\)中出现一次的子串的最短长度

朴素的想法是,我们要找到那些只出现一次的子串,之后遍历每个串,把串所覆盖的区域区间和串长取\(min\)

考虑优化,根据\(s\)串先建立\(SAM\),然后计算出每个状态的\(endpos\)集合的大小,其中大小为\(1\)的状态所表示的一系列子串必然只在原串中出现一次,对于\(endpos\)大小为\(1\)的某个状态\(u\),其表示的子串的最短长度为\(len_{link_u}+1\),最长长度为\(len_u\),假设子串结束的位置为\(firstpos_u\),那么对于\([firstpos_u-len_{link_u}+1,firstpos_u]\)这段区间,需要和\(len_{link_u}\)取\(min\),而对于区间\([firstpos_u-len_u+1,firstpos_u-len_{link_u}]\)来说,区间上的每个位置\(i\)要和\(firstpos_u-i+1\)取\(min\),可以在更新的时候只考虑\(firstpos_u\)的贡献,最后计算的时候在减去\(i-1\)即可,所以根据上述方法,需要建立两棵线段树来维护,其中区间取\(min\)可以通过先排序然后直接赋值来解决

  1. //#pragma GCC optimize("O3")
  2. //#pragma comment(linker, "/STACK:1024000000,1024000000")
  3. #include<bits/stdc++.h>
  4. using namespace std;
  5. function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
  6. const int MAXN = 2e5+7;
  7. char s[MAXN];
  8. struct SegmentTree{
  9. int lazy[MAXN<<2],l[MAXN<<2],r[MAXN<<2];
  10. #define ls(rt) rt << 1
  11. #define rs(rt) rt << 1 | 1
  12. void pushdown(int rt){
  13. if(!lazy[rt]) return;
  14. lazy[ls(rt)] = lazy[rt]; lazy[rs(rt)] = lazy[rt];
  15. lazy[rt] = 0;
  16. }
  17. void build(int L, int R, int rt = 1){
  18. l[rt] = L; r[rt] = R;
  19. if(l[rt] + 1 == r[rt]){
  20. lazy[rt] = MAXN;
  21. return;
  22. }
  23. int mid = (L + R) >> 1;
  24. build(L,mid,ls(rt)); build(mid,R,rs(rt));
  25. }
  26. void update(int L, int R, int x, int rt = 1){
  27. if(l[rt]>=R or L>=r[rt]) return;
  28. if(L<=l[rt] and r[rt]<=R){
  29. lazy[rt] = x;
  30. return;
  31. }
  32. pushdown(rt);
  33. update(L,R,x,ls(rt)); update(L,R,x,rs(rt));
  34. }
  35. int query(int pos, int rt = 1){
  36. if(l[rt] + 1 == r[rt]) return lazy[rt];
  37. int mid = (l[rt] + r[rt]) >> 1;
  38. pushdown(rt);
  39. if(pos<mid) return query(pos,ls(rt));
  40. else return query(pos,rs(rt));
  41. }
  42. }ST1,ST2;
  43. struct SAM{
  44. int len[MAXN],link[MAXN],ch[MAXN][26],tot,last,cnt[MAXN],c[MAXN],sa[MAXN],firstpos[MAXN];
  45. SAM(){ link[0] = -1; }
  46. void extend(int c){
  47. int np = ++tot, p = last;
  48. firstpos[np] = len[np] = len[p] + 1; cnt[np] = 1;
  49. while(p!=-1 and !ch[p][c]){
  50. ch[p][c] = np;
  51. p = link[p];
  52. }
  53. if(p==-1) link[np] = 0;
  54. else{
  55. int q = ch[p][c];
  56. if(len[p]+1==len[q]) link[np] = q;
  57. else{
  58. int clone = ++tot;
  59. len[clone] = len[p] + 1;
  60. link[clone] = link[q];
  61. firstpos[clone] = firstpos[q];
  62. memcpy(ch[clone],ch[q],sizeof(ch[q]));
  63. link[np] = link[q] = clone;
  64. while(p!=-1 and ch[p][c]==q){
  65. ch[p][c] = clone;
  66. p = link[p];
  67. }
  68. }
  69. }
  70. last = np;
  71. }
  72. void Radix_sort(){
  73. for(int i = 0; i <= tot; i++) c[i] = 0;
  74. for(int i = 0; i <= tot; i++) c[len[i]]++;
  75. for(int i = 1; i <= tot; i++) c[i] += c[i-1];
  76. for(int i = tot; i >= 0; i--) sa[c[len[i]]--] = i;
  77. }
  78. void solve(char *s){
  79. int l = strlen(s);
  80. for(int i = 0; i < l; i++) extend(s[i]-'a');
  81. Radix_sort();
  82. for(int i = tot + 1; i > 1; i--) cnt[link[sa[i]]] += cnt[sa[i]];
  83. vector<pair<int,pair<int,int> > > vec;
  84. for(int i = 1; i <= tot; i++) if(cnt[i]==1) vec.emplace_back(make_pair(firstpos[i],make_pair(len[link[i]]+1,len[i])));
  85. ST1.build(1,l+1); ST2.build(1,l+1);
  86. sort(vec.begin(),vec.end(),[](const pair<int,pair<int,int>> &lhs, const pair<int,pair<int,int>> &rhs){
  87. return lhs.second.first > rhs.second.first;
  88. });
  89. for(int i = 0; i < (int)vec.size(); i++) ST1.update(vec[i].first-vec[i].second.first+1,vec[i].first+1,vec[i].second.first);
  90. sort(vec.begin(),vec.end(),[](const pair<int,pair<int,int>> &lhs, const pair<int,pair<int,int>> &rhs){
  91. return lhs.first > rhs.first;
  92. });
  93. for(int i = 0; i < (int)vec.size(); i++) ST2.update(vec[i].first-vec[i].second.second+1,vec[i].first-vec[i].second.first+1,vec[i].first);
  94. for(int i = 1; i <= l; i++) printf("%d\n",min(ST1.query(i),ST2.query(i)-i+1));
  95. }
  96. }sam;
  97. int main(){
  98. scanf("%s",s);
  99. sam.solve(s);
  100. return 0;
  101. }

BZOJ1396 识别子串【SAM+SegmentTree】的更多相关文章

  1. BZOJ1396:识别子串(SAM)

    Description Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. Sample I ...

  2. BZOJ-1396: 识别子串

    后缀自动机+线段树 先建出\(sam\),统计一遍每个点的\(right\)集合大小\(siz\),对于\(siz=1\)的点\(x\),他所代表的子串只会出现一次,设\(y=fa[x]\),则这个点 ...

  3. BZOJ1396 识别子串 和 BZOJ2865 字符串识别

    字符串识别 2865: 字符串识别 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 839  Solved: 261[Submit][Status][D ...

  4. bzoj千题计划318:bzoj1396: 识别子串(后缀自动机 + 线段树)

    https://www.lydsy.com/JudgeOnline/problem.php?id=1396 后缀自动机的parent树上,如果不是叶子节点,那么至少有两个子节点 而一个状态所代表子串的 ...

  5. BZOJ1396: 识别子串(后缀自动机,线段树)

    Description Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. Sample I ...

  6. BZOJ1396 识别子串 字符串 SAM 线段树

    原文链接http://www.cnblogs.com/zhouzhendong/p/9004467.html 题目传送门 - BZOJ1396 题意 给定一个字符串$s$,$|s|\leq 10^5$ ...

  7. bzoj1396识别子串(SAM+线段树)

    复习SAM板子啦!考前刷水有益身心健康当然这不是板子题/水题…… 很容易发现只在i位置出现的串一定是个前缀串.那么对答案的贡献分成两部分:一部分是len[x]-fa~len[x]的这部分贡献会是r-l ...

  8. BZOJ bzoj1396 识别子串

    题面: bzoj1396 题解: 先建出SAM,并计算right集合大小.显然符合条件的点的right集合大小为1. 对于每个right集合为1的状态显然可以算出这些状态的pos以及maxlen和mi ...

  9. bzoj1396: 识别子串

    #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #i ...

随机推荐

  1. 【Java集合】HashSet源码解析以及HashSet与HashMap的区别

    HashSet 前言 HashSet是一个不可重复且元素无序的集合.内部使用HashMap实现. 我们可以从HashSet源码的类注释中获取到如下信息: 底层基于HashMap实现,所以迭代过程中不能 ...

  2. 基于腾讯云存储网关 CSG 实现视频在线转码分发

    一.背景 随着越来越多的传统业务云化和云端业务发展,数据上云和云端数据处理领域的需求爆发式增长.腾讯云存储网关CSG提供一键部署开箱即用的便捷模式,深度结合COS对象存储生态,为用户提供方便快捷的数据 ...

  3. redis 5.0.5 安装

    redis 5.0.5 安装脚本: #!/bin/bash cd /data/src/ test -e tcl8.6.9-src.tar.gz || wget http://downloads.sou ...

  4. 基于Python实现的系统SLA可用性统计

    基于Python实现的系统SLA可用性统计 1. 介绍 SLA是Service Level Agreement的英文缩写,也叫服务质量协议.根据SRE Google运维解密一书中的定义: SLA是服务 ...

  5. rm: cannot remove `/tmp/localhost-mysql_cacti_stats.txt': Operation not permitted

    [root@DBslave tmp]# chown zabbix.zabbix /tmp/localhost-mysql_cacti_stats.txt

  6. xtrabackup 备份与恢复

    书上摘抄 ---深入浅出mysql 448页  grant reload on *.* to 'backup'@'localhost' identified by '123456'; grant re ...

  7. XEE - Pikachu

    概述 XXE -"xml external entity injection"既"xml外部实体注入漏洞".概括一下就是"攻击者通过向服务器注入指定的 ...

  8. Android之旅2

    一.动静态调试四大组件 (一).activity 一个又一个的界面,需要在manifest里面注册 (二). (三).service (四).broadcast receiver 二.开始分析 1.先 ...

  9. C#使用ODP.NET连接oracle数据库

    ODP.NET:Oracle Data Provider for .NET 分为三种: ODP.NET, Managed Driver 不需要安装oracle客户端 ODP.NET,Unmanaged ...

  10. WeihanLi.Npoi 1.14.0 Release Notes

    WeihanLi.Npoi 1.14.0 Release Notes Intro 周末更新了一下项目,开始使用可空引用类型,并且移除了 net45 的支持,仅支持 netstandard2.0 Cha ...