bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)

bzoj Luogu

给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ 有多少本质不同的子串不是 $ S[l,r] $ 的子串。

题解时间

上一道题有点像哈。

只不过这一次是要将 $ T $ 放在 $ S $ 上匹配。

我们先不管每次选取的 $ S $ 段不同,就假设我们已经建好了选取的 $ S $ 段的SAM(也就是前68pts啦)

我们直接把 $ T $ 放上去匹配,维护现在匹配上的最长长度 $ lnow $ ,当 $ px $ 跳到 $ tranc $ 时 $ lnow++ $ ,

当失配时 $ px $ 直接跳到 $ pre $ , $ lnow $ 修改为对应点的 $ len $ 。

但很明显不能每次重新建一遍 $ S $ 的SAM。

所以考虑让上面的匹配操作可以查询在 $ [l,r] $ 区间内某点有无 $ endpos $ 。

写个线段树合并就好。

然后此时上面匹配操作的“失配”步骤需要改一下。

正常是跳到 $ pre $ 后直接修改 $ lnow $ ,但这里不行。

因为:假设我们匹配到某点 $ x $ ,字符串 $ T $ 上 $ ch $ , $ lnow $ 已知,

我们要查询的就是 $ tranc[x][ch] $ 的 $ [l+lnow,r] $ 区间。

所以失配时改成 $ lnow-- $ ,直到 $ lnow==len[pre[x]] $ 时才跳 $ pre $ 。

很明显这样匹配依然是 $ O(nlogn) $ ,那个log来自线段树。

然后就完事了。

设 $ T $ 上匹配第 $ i $ 个字符后的 $ lnow=ma[i] $ ,

则 $ ans=\Sigma (T的SAM上每一个节点) len[x]-max(len[pre[x]],ma[ip[x]]) $

( $ ip[x] $ 指TSAM上 $ x $ 点对应 $ T $ 的哪一位字符)

丑陋的封装-1000

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long lint;
  4. namespace RKK
  5. {
  6. const int N=2000011;
  7. int TAT;
  8. char s0[N],s1[N];int l0,l1;
  9. int rt[N],tcnt,lson[N*44],rson[N*44];
  10. struct sumireko{int to,ne;}e[N];int he[N],ecnt;
  11. void addline(int f,int t){e[++ecnt].to=t;e[ecnt].ne=he[f],he[f]=ecnt;}
  12. void insert(int x,int &px,int pl,int pr)
  13. {
  14. if(!px) px=++tcnt;
  15. if(pl==pr) return;
  16. int pm=pl+pr>>1;
  17. if(x<=pm) insert(x,lson[px],pl,pm);
  18. else insert(x,rson[px],pm+1,pr);
  19. }
  20. int merge(int px,int py,int pl,int pr)
  21. {
  22. if(!px||!py) return px|py;
  23. int pz=++tcnt;
  24. if(pl!=pr)
  25. {
  26. int pm=pl+pr>>1;
  27. lson[pz]=merge(lson[px],lson[py],pl,pm);
  28. rson[pz]=merge(rson[px],rson[py],pm+1,pr);
  29. }
  30. return pz;
  31. }
  32. int query(int l,int r,int px,int pl,int pr)
  33. {
  34. if(l>r||!px) return 0;
  35. if(l<=pl&&r>=pr) return 1;
  36. int pm=pl+pr>>1;
  37. int ret=0;
  38. if(l<=pm) ret|=query(l,r,lson[px],pl,pm);
  39. if(r>pm) ret|=query(l,r,rson[px],pm+1,pr);
  40. return ret;
  41. }
  42. struct remilia{int tranc[26],len,pre;};
  43. int ma[N];
  44. struct sakuya
  45. {
  46. remilia s[N];
  47. int ip[N];
  48. int fin,size;
  49. sakuya(){fin=size=1;}
  50. void set(){memset(s,0,sizeof(remilia)*(size+10)),memset(ip,0,sizeof(int)*(size+10)),fin=size=1;}
  51. void ins(int ch,int i=0)
  52. {
  53. int npx,npy,lpx,lpy;
  54. npx=++size;
  55. s[npx].len=s[fin].len+1;ip[npx]=i;
  56. for(lpx=fin;lpx&&!s[lpx].tranc[ch];lpx=s[lpx].pre) s[lpx].tranc[ch]=npx;
  57. if(!lpx) s[npx].pre=1;
  58. else
  59. {
  60. lpy=s[lpx].tranc[ch];
  61. if(s[lpy].len==s[lpx].len+1) s[npx].pre=lpy;
  62. else
  63. {
  64. npy=++size;
  65. s[npy]=s[lpy],ip[npy]=ip[lpy];
  66. s[npy].len=s[lpx].len+1;
  67. s[npx].pre=s[lpy].pre=npy;
  68. while(s[lpx].tranc[ch]==lpy)
  69. {
  70. s[lpx].tranc[ch]=npy;
  71. lpx=s[lpx].pre;
  72. }
  73. }
  74. }
  75. fin=npx;
  76. }
  77. void dfs(int x)
  78. {
  79. for(int i=he[x],t=e[i].to;i;i=e[i].ne,t=e[i].to) dfs(t),rt[x]=merge(rt[x],rt[t],1,l0);
  80. }
  81. void work()
  82. {
  83. for(int i=2;i<=size;i++) addline(s[i].pre,i);
  84. dfs(1);
  85. }
  86. }sam,sam1;
  87. void work(int l,int r)
  88. {
  89. sam1.set();
  90. for(int i=1;i<=l1;i++) sam1.ins(s1[i]-'a',i);
  91. int px=1,lnow=0;
  92. for(int i=1;i<=l1;i++)
  93. {
  94. while(px!=1&&!query(l+lnow,r,rt[sam.s[px].tranc[s1[i]-'a']],1,l0))
  95. {
  96. lnow--;
  97. if(lnow==sam.s[sam.s[px].pre].len) px=sam.s[px].pre;
  98. }
  99. if(query(l+lnow,r,rt[sam.s[px].tranc[s1[i]-'a']],1,l0))
  100. {
  101. lnow++;
  102. px=sam.s[px].tranc[s1[i]-'a'];
  103. }
  104. ma[i]=lnow;
  105. }
  106. lint ans=0;
  107. for(int i=2;i<=sam1.size;i++) ans+=max(0,sam1.s[i].len-max(sam1.s[sam1.s[i].pre].len,ma[sam1.ip[i]]));
  108. printf("%lld\n",ans);
  109. memset(ma,0,sizeof(int)*(l1+5));
  110. }
  111. int Iris()
  112. {
  113. scanf("%s",s0+1),l0=strlen(s0+1);
  114. for(int i=1;i<=l0;i++) sam.ins(s0[i]-'a'),insert(i,rt[sam.fin],1,l0);
  115. sam.work();
  116. scanf("%d",&TAT);
  117. for(int rkk=1,l,r;rkk<=TAT;rkk++)
  118. {
  119. scanf("%s%d%d",s1+1,&l,&r),l1=strlen(s1+1);
  120. work(l,r);
  121. }
  122. return 0;
  123. }
  124. }
  125. int main(){return RKK::Iris();}

bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)的更多相关文章

  1. BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并

    题目链接: [Noi2018]你的名字 题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字 ...

  2. BZOJ.5417.[NOI2018]你的名字(后缀自动机 线段树合并)

    LOJ 洛谷 BZOJ 考虑\(l=1,r=|S|\)的情况: 对\(S\)串建SAM,\(T\)在上面匹配,可以得到每个位置\(i\)的后缀的最长匹配长度\(mx[i]\). 因为要去重,对\(T\ ...

  3. luogu4770 [NOI2018]你的名字 后缀自动机 + 线段树合并

    其实很水的一道题吧.... 题意是:每次给定一个串\(T\)以及\(l, r\),询问有多少个字符串\(s\)满足,\(s\)是\(T\)的子串,但不是\(S[l .. r]\)的子串 统计\(T\) ...

  4. NOI 2018 你的名字 (后缀自动机+线段树合并)

    题目大意:略 令$ION2017=S,ION2018=T$ 对$S$建$SAM$,每次都把$T$放进去跑,求出结尾是i的前缀串,能匹配上$S$的最长后缀长度为$f_{i}$ 由于$T$必须在$[l,r ...

  5. [NOI2018]你的名字(后缀自动机+线段树)

    题目描述 小A 被选为了ION2018 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外的工作都做好了. 由于ION 已经举办了很多届,所以在题目命名上也是有规定的,ION 命题手 ...

  6. BZOJ3413: 匹配(后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...

  7. cf666E. Forensic Examination(广义后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...

  8. [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)

    https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...

  9. 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)

    模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...

随机推荐

  1. Haar小波分析

    一 尺度函数与小波函数 基本尺度函数定义为:,对其向右平移任意 k 个单位,构成函数族 , 该函数族在 空间中正交,证明如下: 1 : 2 当 m 不等于 k 时, 函数族  构成一组正交基,并形成  ...

  2. 私有化轻量级持续集成部署方案--03-部署web服务(下)

    提示:本系列笔记全部存在于 Github, 可以直接在 Github 查看全部笔记 配置接口代理 前后端分离情况下,前端请求后端接口最常用的一种方式就是使用反向代理,反向代理会让浏览器认为是同源路径, ...

  3. FreeHttp2.3升级说明

    一.升级方法 下载新版本插件 https://files.cnblogs.com/files/lulianqi/FreeHttp2.3.0zip  或 http://lulianqi.com/file ...

  4. [题解]UVA10269 Adventure of Super Mario

    链接:http://vjudge.net/problem/viewProblem.action?id=24902 描述:由城镇.村子和双向边组成的图,从A+B走到1,要求最短路.有K次瞬移的机会,距离 ...

  5. 商业智能BI必备的特性

    商业智能BI的本质对企业来说,商业智能BI不能直接产生决策,而是利用BI工具处理后的数据来支持决策.核心是通过构建数据仓库平台,有效整合数据.组织数据,为分析决策提供支持并实现其价值. 传统的DW/O ...

  6. 【C# 表达式树 三】ExpressionType 节点类型种类

    // // 摘要: // 描述表达式目录树的节点的节点类型. public enum ExpressionType { // // 摘要: // 加法运算,如 a + b,针对数值操作数,不进行溢出检 ...

  7. Oracle sqlplus 常用命令总结

    转至:https://blog.csdn.net/iteye_20746/article/details/81801309?utm_medium=distribute.pc_relevant.none ...

  8. ansible复习笔记_基础-从零到无

    --创建时间:2021年1月25日 --修改时间:2021年3月9日 --作者:飞翔的小胖猪 前言 该文档仅作为作者复习ansible使用,对格式和流程没有做过多的编排和概述.不喜勿喷. 基础 ans ...

  9. Python:numpy.ma模块

    翻译总结自:The numpy.ma module - NumPy v1.21 Manual 前言 ma是Mask的缩写,关于Mask的解释,如果有PS的基础,可以理解为蒙版,如果有计算机网络的基础, ...

  10. Pandas:各种错误

    1.输出为CSV文件时,Permission denied 原因可能是: (1).构建DataFrame时没有写index参数 (2).用Dict构建最开始的数据时,value没有写成List的形式, ...