E. Forensic Examination

time limit per test:6 seconds
memory limit per test:768 megabytes
input:standard input
output:standard output

The country of Reberland is the archenemy of Berland. Recently the authorities of Berland arrested a Reberlandian spy who tried to bring the leaflets intended for agitational propaganda to Berland illegally . The most leaflets contain substrings of the Absolutely Inadmissible Swearword and maybe even the whole word.

Berland legal system uses the difficult algorithm in order to determine the guilt of the spy. The main part of this algorithm is the following procedure.

All the m leaflets that are brought by the spy are numbered from 1 to m. After that it's needed to get the answer to q queries of the following kind: "In which leaflet in the segment of numbers [l, r] the substring of the Absolutely Inadmissible Swearword [pl, pr] occurs more often?".

The expert wants you to automate that procedure because this time texts of leaflets are too long. Help him!

Input

The first line contains the string s (1 ≤ |s| ≤ 5·105) — the Absolutely Inadmissible Swearword. The string s consists of only lowercase English letters.

The second line contains the only integer m (1 ≤ m ≤ 5·104) — the number of texts of leaflets for expertise.

Each of the next m lines contains the only string ti — the text of the i-th leaflet. The sum of lengths of all leaflet texts doesn't exceed 5·104. The text of the leaflets consists of only lowercase English letters.

The next line contains integer q (1 ≤ q ≤ 5·105) — the number of queries for expertise.

Finally, each of the last q lines contains four integers lrplpr (1 ≤ l ≤ r ≤ m, 1 ≤ pl ≤ pr ≤ |s|), where |s| is the length of the Absolutely Inadmissible Swearword.

Output

Print q lines. The i-th of them should contain two integers — the number of the text with the most occurences and the number of occurences of the substring [pl, pr] of the string s. If there are several text numbers print the smallest one.

Examples
input
  1. suffixtree
    3
    suffixtreesareawesome
    cartesiantreeisworsethansegmenttree
    nyeeheeheee
    2
    1 2 1 10
    1 3 9 10
output
  1. 1 1
    3 4

Solution

题目大意:给出一个模板串S和M个特殊串,每次询问S的[l,r]这个子串出现在编号为[pl,pr]的特殊串中最多出现次数以及其编号。

显然可以把所有串连起来建后缀自动机。

对于每个特殊串的节点,可以认为它包含一种颜色,然后查询操作实际上就是查询一个子树颜色数。

这个可以从叶子节点利用线段树合并得到;

总的复杂度是$O(NlogN)$

Code

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cmath>
  4. #include<algorithm>
  5. #include<cstring>
  6. using namespace std;
  7. inline int read()
  8. {
  9. int x=0,f=1; char ch=getchar();
  10. while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
  11. while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
  12. return x*f;
  13. }
  14.  
  15. #define MAXN 500010
  16. #define MAXS 1230010
  17.  
  18. int N,M,Q,pos[MAXN];
  19.  
  20. char S[MAXN],s[MAXN];
  21.  
  22. int root=1,sz=1,last=1,par[MAXS],son[MAXS][27],len[MAXS],st[MAXS],id[MAXS];
  23. inline void Extend(int c)
  24. {
  25. int cur=++sz,p=last;
  26. len[cur]=len[p]+1;
  27. while (p && !son[p][c]) son[p][c]=cur,p=par[p];
  28. if (!p) par[cur]=root;
  29. else {
  30. int q=son[p][c];
  31. if (len[p]+1==len[q]) par[cur]=q;
  32. else {
  33. int nq=++sz;
  34. memcpy(son[nq],son[q],sizeof(son[nq]));
  35. len[nq]=len[p]+1; par[nq]=par[q];
  36. while (p && son[p][c]==q) son[p][c]=nq,p=par[p];
  37. par[q]=par[cur]=nq;
  38. }
  39. }
  40. last=cur;
  41. }
  42.  
  43. inline void Sort()
  44. {
  45. for (int i=0; i<=sz; i++) st[i]=0;
  46. for (int i=1; i<=sz; i++) st[len[i]]++;
  47. for (int i=0; i<=sz; i++) st[i]+=st[i-1];
  48. for (int i=1; i<=sz; i++) id[st[len[i]]--]=i;
  49. }
  50.  
  51. #define Pa pair<int,int>
  52. #define MP make_pair
  53. #define Max first
  54. #define Id second
  55.  
  56. struct SgtNode{
  57. Pa key;
  58. int lson,rson;
  59. }tree[MAXS*23];
  60.  
  61. inline Pa operator * (const Pa & A,const Pa &B) {return A.Max==B.Max? (A.Id<B.Id? A:B):(A.Max>B.Max? A:B);}
  62.  
  63. inline Pa operator + (const Pa & A,const Pa &B) {return MP(A.Max+B.Max,A.Id);}
  64.  
  65. int cnt,roots[MAXS],father[21][MAXS];
  66. inline void Insert(int &x,int val,int l,int r)
  67. {
  68. if(!x) x=++cnt;
  69. if (l==r) {tree[x].key.Max++,tree[x].key.Id=l; return;}
  70. int mid=(l+r)>>1;
  71. if (val<=mid) Insert(tree[x].lson,val,l,mid);
  72. else Insert(tree[x].rson,val,mid+1,r);
  73. tree[x].key=tree[tree[x].lson].key * tree[tree[x].rson].key;
  74. }
  75.  
  76. inline int Merge(int x,int y,int l,int r)
  77. {
  78. if (!x || !y) return x|y;
  79. int z=++cnt;
  80. if (l==r) {
  81. tree[z].key=tree[x].key+tree[y].key;
  82. return z;
  83. }
  84. int mid=(l+r)>>1;
  85. tree[z].lson=Merge(tree[x].lson,tree[y].lson,l,mid);
  86. tree[z].rson=Merge(tree[x].rson,tree[y].rson,mid+1,r);
  87. tree[z].key=tree[tree[z].lson].key * tree[tree[z].rson].key;
  88. return z;
  89. }
  90.  
  91. inline Pa Query(int x,int l,int r,int L,int R)
  92. {
  93. if (!x) return MP(0,0);
  94. if (L<=l && R>=r) return tree[x].key;
  95. int mid=(l+r)>>1;
  96. if (R<=mid) return Query(tree[x].lson,l,mid,L,R);
  97. else if (L>mid) return Query(tree[x].rson,mid+1,r,L,R);
  98. else return Query(tree[x].lson,l,mid,L,mid) * Query(tree[x].rson,mid+1,r,mid+1,R);
  99. }
  100.  
  101. inline int Get(int l,int r)
  102. {
  103. int Len=r-l+1,x=pos[r];
  104. for (int i=20; i>=0; i--)
  105. if (len[father[i][x]]>=Len)
  106. x=father[i][x];
  107. return x;
  108. }
  109.  
  110. int main()
  111. {
  112. scanf("%s",S+1); N=strlen(S+1);
  113. for (int i=1; i<=N; i++) Extend(S[i]-'a'),pos[i]=last;
  114. Extend(26);
  115. M=read();
  116. for (int j=1; j<=M; j++) {
  117. scanf("%s",s+1); int le=strlen(s+1);
  118. for (int i=1; i<=le; i++) {
  119. Extend(s[i]-'a'),Insert(roots[last],j,1,M);
  120. }
  121. Extend(26);
  122. }
  123.  
  124. Sort();
  125.  
  126. for (int i=sz; i>=1; i--) {
  127. int x=id[i];
  128. if (par[x]) roots[par[x]]=Merge(roots[par[x]],roots[x],1,M);
  129. }
  130.  
  131. for (int i=1; i<=sz; i++) father[0][i]=par[i];
  132.  
  133. for (int j=1; j<=20; j++)
  134. for (int i=1; i<=sz; i++)
  135. father[j][i]=father[j-1][father[j-1][i]];
  136.  
  137. Q=read();
  138. while (Q--) {
  139. int l=read(),r=read(),pl=read(),pr=read();
  140. int x=Get(pl,pr);
  141. Pa ans=Query(roots[x],1,M,l,r);
  142. if (!ans.Max) printf("%d %d\n",l,0);
  143. else printf("%d %d\n",ans.Id,ans.Max);
  144. }
  145.  
  146. return 0;
  147. }

  

【Codeforces666E】Forensic Examination 后缀自动机 + 线段树合并的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

  7. CF 666E Forensic Examination——广义后缀自动机+线段树合并

    题目:http://codeforces.com/contest/666/problem/E 对模式串建广义后缀自动机,询问的时候把询问子串对应到广义后缀自动机的节点上,就处理了“区间”询问. 还要处 ...

  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\)的时候先 ...

随机推荐

  1. 读书笔记:《思考的乐趣:Matrix67数学笔记》第4章 统计数据的陷阱

    <思考的乐趣:Matrix67数学笔记>第4章讲了几个统计学上的陷阱,由于现在流行的大数据与统计学很有渊源,所以认真读了这一章,在<大数据时代>中指出只考虑相关性就够了,而不考 ...

  2. 第12月第8天 Retrofit.builder

    1. retrofit = new Retrofit.Builder() .client(okHttpClient) .addConverterFactory(GsonConverterFactory ...

  3. mysql 1045 access denied for user 解决方法

    提示:1045 access denied for user 'root'@'localhost' using password yes方法一: # /etc/init.d/mysql stop #  ...

  4. 云计算-MapReduce

    Hadoop示例程序WordCount详解及实例http://blog.csdn.net/xw13106209/article/details/6116323 hadoop中使用MapReduce编程 ...

  5. python enumerate用法总结【转】

    enumerate()说明 enumerate()是python的内置函数 enumerate在字典上是枚举.列举的意思 对于一个可迭代的(iterable)/可遍历的对象(如列表.字符串),enum ...

  6. mac安装sentry

    最近需要一个日志监视系统所以选择了sentry. 安装docker https://download.docker.com/mac/stable/Docker.dmg 登录 安装完了打开 会提示登录输 ...

  7. OpenLayers 3 之 地图图层数据来源(ol.source)详解

    原文地址 source 是 Layer 的重要组成部分,表示图层的来源,也就是服务地址.除了在构造函数中制定外,可以使用 layer.setSource(source) 稍后指定.一.包含的类型 ol ...

  8. centos7下安装配置redis3.0.4

    安装redis 1.进入redis官网(redis.io)下载redis稳定版安装包,目前稳定版本为3.0.4 2.在linux  /usr文件夹下新建redis文件夹,拷贝安装包redis-3.0. ...

  9. H5新增语义化标签

    一.根据页面的结构,由div派生的标签. <header> <footer> <nav> 导航 在H4中导航栏一般用ul-li标签,H5中可以直接用<nav& ...

  10. 发布构件到Maven中央仓库

    一.注册jira账号 访问如下网址: https://issues.sonatype.org/secure/Signup.jspa 记住邮箱,用户名,密码以备以后使用,一定牢记. 二.创建一个issu ...