字符串

佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CEO,嫁给高富帅,走上人生巅峰。每个问题均有a,b,c,d四个参数,问你子串s[a..b]的所有子串和s[c..d]的最长公共前缀的长度的最大值是多少?佳媛姐姐并不擅长做这样的问题,所以她向你求助,你该如何帮助她呢?

1<=n,m<=100,000,

分析

参照Dream_maker_ykwzj的题解。

查前缀我们就把串反着插入,这时候状态该有一个left集合。

在这个反串的SAM上,两个点的最长公共前缀是两个点parent树上lca的len.

我们对于parent树中的每个节点,都维护他的子树中出现了字符串中的哪些节点,即left集合。这个可用线段树合并。

二分答案x,倍增找到c的祖先中len>=x的最浅的节点,判断该节点的left集合中是否出现了[a,b-x+1]。

时间复杂度\(O(n \log^2 n)\)

  1. co int N=2e5;
  2. namespace T{ // Interval Tree
  3. int tot,root[N],lc[N*20],rc[N*20];
  4. void insert(int&x,int l,int r,int p){
  5. x=++tot;
  6. if(l==r) return;
  7. int mid=l+r>>1;
  8. if(p<=mid) insert(lc[x],l,mid,p);
  9. else insert(rc[x],mid+1,r,p);
  10. }
  11. int merge(int x,int y){
  12. if(!x||!y) return x+y;
  13. int z=++tot;
  14. lc[z]=merge(lc[x],lc[y]),rc[z]=merge(rc[x],rc[y]);
  15. return z;
  16. }
  17. int query(int x,int l,int r,int ql,int qr){
  18. if(!x) return 0;
  19. if(ql<=l&&r<=qr) return 1;
  20. int mid=l+r>>1;
  21. if(qr<=mid) return query(lc[x],l,mid,ql,qr);
  22. if(ql>mid) return query(rc[x],mid+1,r,ql,qr);
  23. return query(lc[x],l,mid,ql,qr)||query(rc[x],mid+1,r,ql,qr);
  24. }
  25. }
  26. int n,m;
  27. char s[N];
  28. int last=1,tot=1; // Suffix Automaton
  29. int ch[N][26],fa[N],len[N],pos[N]; // pos: out->in
  30. void extend(int c,int po){
  31. int p=last,cur=last=++tot;
  32. len[cur]=len[p]+1,pos[po]=cur;
  33. T::insert(T::root[cur],1,n,po);
  34. for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=cur;
  35. if(!p) fa[cur]=1;
  36. else{
  37. int q=ch[p][c];
  38. if(len[q]==len[p]+1) fa[cur]=q;
  39. else{
  40. int clone=++tot;
  41. memcpy(ch[clone],ch[q],sizeof ch[q]);
  42. fa[clone]=fa[q],len[clone]=len[p]+1;
  43. fa[cur]=fa[q]=clone;
  44. for(;ch[p][c]==q;p=fa[p]) ch[p][c]=clone;
  45. }
  46. }
  47. }
  48. int cnt[N],ord[N],anc[N][19];
  49. int check(int x,int a,int b,int p){
  50. for(int i=18;i>=0;--i)
  51. if(len[anc[p][i]]>=x) p=anc[p][i];
  52. return T::query(T::root[p],1,n,a,b-x+1);
  53. }
  54. int main(){
  55. read(n),read(m);
  56. scanf("%s",s+1);
  57. for(int i=n;i>=1;--i) extend(s[i]-'a',i);
  58. for(int i=1;i<=tot;++i) ++cnt[len[i]];
  59. for(int i=1;i<=n;++i) cnt[i]+=cnt[i-1];
  60. for(int i=1;i<=tot;++i) ord[cnt[len[i]]--]=i;
  61. for(int i=tot;i;--i){
  62. int u=ord[i];
  63. T::root[fa[u]]=T::merge(T::root[fa[u]],T::root[u]);
  64. }
  65. for(int i=1;i<=tot;++i){ // edit 1: order isn't changeable
  66. int u=ord[i];
  67. anc[u][0]=fa[u];
  68. for(int j=1;j<=18;++j) anc[u][j]=anc[anc[u][j-1]][j-1];
  69. }
  70. for(int a,b,c,d;m--;){
  71. read(a),read(b),read(c),read(d);
  72. int l=0,r=std::min(b-a+1,d-c+1);
  73. while(l<r){
  74. int mid=l+r+1>>1;
  75. if(check(mid,a,b,pos[c])) l=mid;
  76. else r=mid-1;
  77. }
  78. printf("%d\n",l);
  79. }
  80. return 0;
  81. }

TJOI2016 字符串的更多相关文章

  1. 洛谷P4094 - [TJOI2016]字符串

    Portal Description 给出一个字符串\(s(|s|\leq10^5)\)和\(m\)次询问,每次询问子串\(s[x_1..x_2]\)的所有子串和\(s[y_1..y_2]\)的最长公 ...

  2. BZOJ 4556 [HEOI2016/TJOI2016]字符串

    BZOJ 4556 [HEOI2016/TJOI2016]字符串 其实题解更多是用后缀数组+数据结构的做法,貌似也不好写. 反正才学了 sam 貌似比较简单的做法. 还是得先二分,然后倍增跳到 $ s ...

  3. P4094 [HEOI2016/TJOI2016]字符串 后缀数组+主席树+二分答案

    $ \color{#0066ff}{ 题目描述 }$ 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为n的字符串s,和m个问题.佳媛姐姐必须 ...

  4. [HEOI2016/TJOI2016]字符串

    嘟嘟嘟 今天复习一下SAM. lcp固然不好做,干脆直接翻过来变成后缀.首先答案一定满足单调性,所以我们二分lcp的长度\(mid\),然后判断\(s[d \ldots d + mid - 1]\)是 ...

  5. [HEOI2016/TJOI2016]字符串(后缀数组+二分+主席树/后缀自动机+倍增+线段树合并)

    后缀数组解法: 先二分最长前缀长度 \(len\),然后从 \(rnk[c]\) 向左右二分 \(l\) 和 \(r\) 使 \([l,r]\) 的 \(height\geq len\),然后在主席树 ...

  6. 【[HEOI2016/TJOI2016]字符串】

    码农题啊 上来先无脑一个\(SA\)的板子,求出\(SA\)和\(het\)数组 我们只需要从\(sa[i]\in[a,b]\)的所有\(i\)中找到一个\(i\)使得\(sa[i]\)和\(rk[c ...

  7. luoguP4094 [HEOI2016/TJOI2016]字符串

    题意 考虑二分答案\(mid\),现在我们要判断\(s[c...c+mid-1]\)是否在\(s[a...b]\)出现过. 首先找到\(s[c...c+mid-1]\)所在的状态: 建出\(paren ...

  8. BZOJ4556 HEOI2016/TJOI2016字符串 (后缀树+主席树)

    二分答案后相当于判断一个区间的后缀与某个后缀的最长公共前缀是否能>=ans.建出后缀树,在上述问题中后者所在节点向上倍增的跳至len>=ans的最高点,然后相当于查询子树中是否有该区间的节 ...

  9. HEOI2016/TJOI2016 字符串问题

    题目链接:戳我 非常不好意思,因为想要排版,所以今天先只把代码贴出来,明天补题解. 40pts暴力:直接暴力匹配 #include<iostream> #include<cstrin ...

随机推荐

  1. win7蓝屏死机0x0000003B错误蓝屏故障解决

    win7蓝屏死机0x0000003B错误蓝屏故障解决 刚才一个朋友问我:电脑蓝屏了怎么办. 我问他要了电脑的截图,自己看了错误代码:0x0000003B 搜索资料,查询了一番.都是说电脑中病毒或者是系 ...

  2. Selenium自动化获取WebSocket信息

    性能日志 ChromeDriver支持性能日志记录,您可以从中获取域“时间轴”,“网络”和“页面”的事件,以及指定跟踪类别的跟踪数据. 启用性能日志 默认情况下不启用性能日志记录.因此,在创建新会话时 ...

  3. 《PHP - 信号/基本操作/配置》

    一:PHP 信号 - SIGINT / SIGTERM / SIGQUIT - 退出FPM,在master收到退出信号后将向所有的worker进程发送退出信号,然后master退出. - SIGUSR ...

  4. 92. 反转链表 II

    反转从位置 m 到 n 的链表.请使用一趟扫描完成反转.   说明: 1 ≤ m ≤ n ≤ 链表长度.   示例: 输入: 1->2->3->4->5->NULL, m ...

  5. docker深入学习二

    dicker:数据管理 数据管理机制 docker使用union file system来管理数据,docker构建image和container也是采用了同样的技术. image层次 iamge由多 ...

  6. Modelsim——do脚本、bat命令

    一.do脚本实现自动化仿真 Modelsim是支持命令的,我们可以用 .do 文件将这些命令先写好然后在Modelsim上调用.因为我的编辑器不支持.do的语法,所以这里改用 .tcl文件,它和 .d ...

  7. iOS核心动画(基础篇)

    Core Animation相关内容基本介绍 此框架把屏幕上的内容组合起来,这个内容被分解成图层,放到图层树中,这个树形成了你能在应用程序看到的内容的基础 图层在iOS中就是CALayer类 当我们创 ...

  8. 【C#】课堂知识点#4

    1.回顾类中基本结构. 成员分为: a.(数据成员) , b.(方法成员) 数据成员: 字段 方法成员:方法,构造函数,属性,索引器,运算符. 属性的作用: 对字段进行访问提供get,set方法. 类 ...

  9. quartz2.3.0(八)使用日历排除不应该执行任务的时间段

    Job任务类 package org.quartz.examples.example8; import java.util.Date; import org.slf4j.Logger; import ...

  10. ultraedit 实际应用技巧

    Tip 1: Alt+C 列模式可以说最初选择使用这个文本编辑软件,原因很简单,就是因为“她”具有列编辑模式.如果您还不知道什么是列编辑模式的话,我想您应该好好研究一下啦.这是一个超级“赞”的功能.在 ...