【CF666E】Forensic Examination(后缀自动机,线段树合并)

题面

洛谷

CF

翻译:

给定一个串\(S\)和若干个串\(T_i\)

每次询问\(S[pl..pr]\)在\(T_l..T_r\)中出现的最多次数,以及出现次数最多的那个串的编号。

题解

好题啊。

我们对于所有的\(T\)串构建出广义后缀自动机之后

把\(S\)丢到\(SAM\)上匹配,对于每组询问,

相当于回答\(S[pl..pr]\)所代表的节点的\(right\)集合所代表的串的众数是哪个串,以及这个众数出现的次数。

考虑如何维护\(right\)集合关于每个\(T\)的出现次数

我们对于每个节点开一棵线段树,线段树的值域是\(1..m\),用来维护每个\(T\)串的出现次数。

每个节点维护的值就是\(T\)串的最多出现次数。

这样就挺好做了,\(right\)集合只需要线段树合并就可以快速求得。

对于每个询问挂链,然后\(dfs\)一遍\(parent\)树的时候,顺便在线段树上查询一下区间最大就行了。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #include<cmath>
  6. #include<algorithm>
  7. #include<set>
  8. #include<map>
  9. #include<vector>
  10. #include<queue>
  11. using namespace std;
  12. #define ll long long
  13. #define RG register
  14. #define MAX 1111111
  15. inline int read()
  16. {
  17. RG int x=0,t=1;RG char ch=getchar();
  18. while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
  19. if(ch=='-')t=-1,ch=getchar();
  20. while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
  21. return x*t;
  22. }
  23. struct SAM
  24. {
  25. struct Node
  26. {
  27. int son[26];
  28. int ff,len;
  29. }t[MAX];
  30. int last,tot;
  31. void init(){last=tot=1;}
  32. void extend(int c)
  33. {
  34. int p=last,np=++tot;last=np;
  35. t[np].len=t[p].len+1;
  36. while(p&&!t[p].son[c])t[p].son[c]=np,p=t[p].ff;
  37. if(!p)t[np].ff=1;
  38. else
  39. {
  40. int q=t[p].son[c];
  41. if(t[q].len==t[p].len+1)t[np].ff=q;
  42. else
  43. {
  44. int nq=++tot;
  45. t[nq]=t[q];
  46. t[nq].len=t[p].len+1;
  47. t[q].ff=t[np].ff=nq;
  48. while(p&&t[p].son[c]==q)t[p].son[c]=nq,p=t[p].ff;
  49. }
  50. }
  51. }
  52. }SAM;
  53. int n,m,f[22][MAX];
  54. struct Data{int v,p;}ans[MAX];
  55. bool operator<(Data a,Data b){return (a.v<b.v)||(a.v==b.v&&a.p>b.p);}
  56. struct SegmentTree{int ls,rs;Data v;}t[MAX<<4];
  57. int Tot,rt[MAX];
  58. struct query{int l,r,pl,pr;}q[MAX];
  59. char S[MAX],T[MAX];
  60. void Modify(int &x,int l,int r,int p)
  61. {
  62. if(!x)x=++Tot;
  63. if(l==r){t[x].v.v++;t[x].v.p=p;return;}
  64. int mid=(l+r)>>1;
  65. if(p<=mid)Modify(t[x].ls,l,mid,p);
  66. else Modify(t[x].rs,mid+1,r,p);
  67. t[x].v=max(t[t[x].ls].v,t[t[x].rs].v);
  68. }
  69. void Merge(int &x,int y)
  70. {
  71. if(!x||!y){x|=y;return;}
  72. if(!t[x].ls&&!t[x].rs){t[x].v.v+=t[y].v.v;return;}
  73. Merge(t[x].ls,t[y].ls);Merge(t[x].rs,t[y].rs);
  74. t[x].v=max(t[t[x].ls].v,t[t[x].rs].v);
  75. }
  76. Data Query(int x,int l,int r,int L,int R)
  77. {
  78. if(L==l&&r==R)return t[x].v;
  79. int mid=(l+r)>>1;
  80. if(R<=mid)return Query(t[x].ls,l,mid,L,R);
  81. if(L>mid)return Query(t[x].rs,mid+1,r,L,R);
  82. return max(Query(t[x].ls,l,mid,L,mid),Query(t[x].rs,mid+1,r,mid+1,R));
  83. }
  84. struct Link
  85. {
  86. struct Line{int v,next;}e[MAX];
  87. int h[MAX],cnt;
  88. void Add(int u,int v){e[++cnt]=(Line){v,h[u]};h[u]=cnt;}
  89. }Par,Qy,Aw;
  90. void dfs(int u)
  91. {
  92. for(int i=Par.h[u];i;i=Par.e[i].next)
  93. dfs(Par.e[i].v),Merge(rt[u],rt[Par.e[i].v]);
  94. for(int i=Aw.h[u];i;i=Aw.e[i].next)
  95. ans[Aw.e[i].v]=Query(rt[u],1,m,q[Aw.e[i].v].l,q[Aw.e[i].v].r);
  96. }
  97. int main()
  98. {
  99. scanf("%s",S+1);n=strlen(S+1);
  100. m=read();SAM.init();
  101. for(int i=1;i<=m;++i)
  102. {
  103. SAM.last=1;scanf("%s",T+1);
  104. for(int j=1,l=strlen(T+1);j<=l;++j)
  105. SAM.extend(T[j]-97),Modify(rt[SAM.last],1,m,i);
  106. }
  107. int Q=read();
  108. for(int i=1;i<=Q;++i)
  109. {
  110. q[i]=(query){read(),read(),read(),read()};
  111. Qy.Add(q[i].pr,i);
  112. }
  113. for(int i=2;i<=SAM.tot;++i)Par.Add(f[0][i]=SAM.t[i].ff,i);
  114. for(int i=1;i<22;++i)
  115. for(int j=1;j<=SAM.tot;++j)f[i][j]=f[i-1][f[i-1][j]];
  116. for(int i=1,nw=1,len=0;i<=n;++i)
  117. {
  118. int c=S[i]-97;
  119. while(nw&&!SAM.t[nw].son[c])nw=SAM.t[nw].ff,len=SAM.t[nw].len;
  120. if(!nw){nw=1,len=0;continue;}
  121. nw=SAM.t[nw].son[c];len+=1;
  122. for(int j=Qy.h[i];j;j=Qy.e[j].next)
  123. {
  124. int v=Qy.e[j].v,u=nw;
  125. if(len<q[v].pr-q[v].pl+1)continue;
  126. for(int k=21;~k;--k)
  127. if(SAM.t[f[k][u]].len>=q[v].pr-q[v].pl+1)u=f[k][u];
  128. Aw.Add(u,v);
  129. }
  130. }
  131. dfs(1);
  132. for(int i=1;i<=Q;++i)
  133. {
  134. if(!ans[i].v)ans[i].p=q[i].l;
  135. printf("%d %d\n",ans[i].p,ans[i].v);
  136. }
  137. return 0;
  138. }

【CF666E】Forensic Examination(后缀自动机,线段树合并)的更多相关文章

  1. 【Codeforces666E】Forensic Examination 后缀自动机 + 线段树合并

    E. Forensic Examination time limit per test:6 seconds memory limit per test:768 megabytes input:stan ...

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

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

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

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

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

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

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

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

  6. 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)

    点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...

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

    bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...

  8. CF666E Forensic Examination(后缀自动机+线段树合并)

    给你一个串S以及一个字符串数组T[1..m],q次询问,每次问S的子串S[pl..pr]在T[l..r]中的哪个串里的出现次数最多,并输出出现次数. 如有多解输出最靠前的那一个. 我们首先对m个字符串 ...

  9. [CF666E]Forensic Examination:后缀自动机+线段树合并

    分析 用到了两个小套路: 使用线段树合并维护广义后缀自动机的\(right\)集合. 查询\(S[L,R]\)在\(T\)中的出现次数:给\(T\)建SAM,在上面跑\(S\),跑到\(R\)的时候先 ...

  10. CF666E Forensic Examination [后缀自动机,线段树合并]

    洛谷 Codeforces 思路 最初的想法:后缀数组+区间众数,似乎并不能过. 既然后缀数组不行,那就按照套路建出广义SAM,然后把\(S\)放在上面跑,得到以每个点结尾会到SAM上哪个节点. 询问 ...

随机推荐

  1. RestQL:现代化的 API 开发方式

    参考:https://tech.meituan.com/koa-restql.html 在现代的业务系统中,后端开发工作基本上可以被拆分为三项: 接口鉴权.例如判断是不是当前系统的用户,以及该用户是否 ...

  2. JS基础,课堂作业,成绩练习

    成绩练习 <script> var name = prompt("请输入学生姓名:"); var degree = parseInt(prompt("请输入学 ...

  3. JAVA高级之路----JAVA多线程

    介绍 这段时间一直在学习和整理一些通往java高级程序猿必备的知识点,有些是工作中必须要知道的,有些是面试必须要知道的, 但是不管怎么样,学习了就不会有坏处,不可能全部记得住,最起码得雁过留痕,知识不 ...

  4. oss上传文件0字节

    最近使用oss上传文件,不同项目中使用的版本也不同,之前的都能正常上传,最近因需要添加ObjectMetaData属性,扩展了一个方法,发现上传的文件始终是0字节的,最终跟源码发现conntentLe ...

  5. 关于Eclipse在servlet中连接数据库时出现驱动加载失败的解决

    问题:在队友发来的项目中想将他获取到的数据通过数据库储存,出现驱动加载失败问题 解决:首先百度了下相关情况,大多数都是说下载mysql-connector-java-5.1.39-bin.jar包,然 ...

  6. 数据库sql优化总结之1-百万级数据库优化方案+案例分析

    项目背景 有三张百万级数据表 知识点表(ex_subject_point)9,316条数据 试题表(ex_question_junior)2,159,519条数据 有45个字段 知识点试题关系表(ex ...

  7. pandas中DataFrame的ix,loc,iloc索引方式的异同

    pandas中DataFrame的ix,loc,iloc索引方式的异同 1.loc: 按照标签索引,范围包括start和end 2.iloc: 在位置上进行索引,不包括end 3.ix: 先在inde ...

  8. Eclipse/myEclipse 代码提示/自动提示/自动完成设置(转)

    一.设置超级自动提示 设置eclipse/myEclipse代码提示可以方便开发者,不用在记住拉杂的单词,只用打出首字母,就会出现提示菜单.如同dreamweaver一样方便. 1.菜单window- ...

  9. Improving the Safety, Scalability, and Efficiency of Network Function State Transfers

    Improving the Safety, Scalability, and Efficiency of Network Function State Transfers 来源:ACM SIGCOMM ...

  10. OOP 2.1 类和对象的基本概念2

    1.成员函数的另一种写法:类的成员函数和类的定义分开写 e.g. class rectangle { public: int w,h; int area(); int p(); void init(i ...