离线处理所有询问。

对于$r-l\leq 50$的情况:

按照串长从$1$到$51$分别把所有子串按照第一位字符为第一关键字,上一次排序结果为第二关键字进行$O(n)$基数排序。

同理也可以用上一次比较结果来判断这一次某两个子串是否相同。

对于每个询问,找到排序结果中对应的区间,在里面二分出起点$x$,问题转化为从$x$开始贪心会一直往右跳最终能跳过多少个位置。

继续离线,预处理出每个$x$的后继$f_x$作为$x$的祖先,DFS这棵树的时候在栈上二分即可。

时间复杂度$O(len\times n+m\log n)$。

对于$r-l\geq 51$的情况:

因为串比较长而且随机,所以匹配次数不会很多,暴力找到所有匹配位置即可。

先在后缀数组上找到对应的区间$[l,r]$,问题转化为在$[l,r]$中找到值比$k$大的最小的值。

设$T_k$表示仅保留所有$\geq k$的值的线段树,在$T_k$中查询区间最小值即可。而$T$可以通过可持久化在$O(n\log n)$的时间内预处理出来。

设$cnt$为匹配次数,则时间复杂度为$O(cnt\log n)$。

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<algorithm>
  4. #include<vector>
  5. using namespace std;
  6. typedef long long ll;
  7. const int N=400010,S=256;
  8. int n,m,mx,now,i,j,x,y,len,val[N],e[N][4];ll ans[N];
  9. char a[N],b[N];
  10. vector<int>que[N],need[N];
  11. int c[N],q[N],id[N],nq[N],nid[N],en[N],g[N],nxt[N];
  12. int pool[N],top;ll sum[N];
  13. inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
  14. inline void small(int o){
  15. int S=e[o][0],T=e[o][1],p=id[e[o][2]+n];
  16. int l=p,r=en[p],mid,t;
  17. while(l<=r){
  18. mid=(l+r)>>1;
  19. if(q[mid]>=S)r=(t=mid)-1;else l=mid+1;
  20. }
  21. if(q[t]+len-1>T)return;
  22. need[q[t]].push_back(o);
  23. }
  24. inline void add(int x,int y){nxt[x]=g[y];g[y]=x;}
  25. inline void query(int o){
  26. int T=e[o][1],l=1,r=top,mid,t=0;
  27. while(l<=r){
  28. mid=(l+r)>>1;
  29. if(pool[mid]+len-1>T)l=(t=mid)+1;else r=mid-1;
  30. }
  31. ans[o]=sum[top]-sum[t];
  32. }
  33. void dfs(int x){
  34. if(x){
  35. pool[++top]=x;
  36. sum[top]=sum[top-1]+val[x];
  37. for(int i=0;i<need[x].size();i++)query(need[x][i]);
  38. }
  39. for(int i=g[x];i;i=nxt[i])dfs(i);
  40. if(x)top--;
  41. }
  42. namespace SA{
  43. const int M=2400000;
  44. int n,rk[N],sa[N],height[N],tmp[N],cnt[N];char s[N];
  45. int Log[N],f[18][200010];
  46. int l[M],r[M],v[M],tot,root[N],fin;
  47. void suffixarray(int n,int m){
  48. int i,j,k;n++;
  49. for(i=0;i<n;i++)cnt[rk[i]=s[i]]++;
  50. for(i=1;i<m;i++)cnt[i]+=cnt[i-1];
  51. for(i=0;i<n;i++)sa[--cnt[rk[i]]]=i;
  52. for(k=1;k<=n;k<<=1){
  53. for(i=0;i<n;i++){
  54. j=sa[i]-k;
  55. if(j<0)j+=n;
  56. tmp[cnt[rk[j]]++]=j;
  57. }
  58. sa[tmp[cnt[0]=0]]=j=0;
  59. for(i=1;i<n;i++){
  60. if(rk[tmp[i]]!=rk[tmp[i-1]]||rk[tmp[i]+k]!=rk[tmp[i-1]+k])cnt[++j]=i;
  61. sa[tmp[i]]=j;
  62. }
  63. memcpy(rk,sa,n*sizeof(int));
  64. memcpy(sa,tmp,n*sizeof(int));
  65. if(j>=n-1)break;
  66. }
  67. for(j=rk[height[i=k=0]=0];i<n-1;i++,k++)
  68. while(~k&&s[i]!=s[sa[j-1]+k])height[j]=k--,j=rk[sa[j]+1];
  69. }
  70. inline int lcp(int x,int y){
  71. if(x>y)swap(x,y);
  72. x++;
  73. int k=Log[y-x+1];
  74. return min(f[k][x],f[k][y-(1<<k)+1]);
  75. }
  76. int build(int a,int b){
  77. int x=++tot;
  78. v[x]=N;
  79. if(a==b)return x;
  80. int mid=(a+b)>>1;
  81. l[x]=build(a,mid);
  82. r[x]=build(mid+1,b);
  83. return x;
  84. }
  85. int ins(int x,int a,int b,int c,int p){
  86. int y=++tot;
  87. v[y]=min(v[x],p);
  88. if(a==b)return y;
  89. int mid=(a+b)>>1;
  90. if(c<=mid)l[y]=ins(l[x],a,mid,c,p),r[y]=r[x];
  91. else l[y]=l[x],r[y]=ins(r[x],mid+1,b,c,p);
  92. return y;
  93. }
  94. void ask(int x,int a,int b,int c,int d){
  95. if(v[x]>=fin)return;
  96. if(c<=a&&b<=d){fin=v[x];return;}
  97. int mid=(a+b)>>1;
  98. if(c<=mid)ask(l[x],a,mid,c,d);
  99. if(d>mid)ask(r[x],mid+1,b,c,d);
  100. }
  101. void init(){
  102. suffixarray(n,S);
  103. for(i=1;i<=n;i++)f[0][i]=height[i];
  104. for(i=2;i<=n;i++)Log[i]=Log[i>>1]+1;
  105. for(j=1;j<18;j++)for(i=1;i+(1<<j)-1<=n;i++)f[j][i]=min(f[j-1][i],f[j-1][i+(1<<(j-1))]);
  106. int m=n>>1;
  107. root[m]=build(1,n);
  108. for(i=m-1;~i;i--)root[i]=ins(root[i+1],1,n,rk[i],i);
  109. }
  110. inline void solve(int o){
  111. int S=e[o][0]-1,T=e[o][1]-1,p=rk[e[o][2]+(n>>1)-1];
  112. int L=p,R=p,l,r,mid;
  113. l=1,r=p-1;
  114. while(l<=r){
  115. mid=(l+r)>>1;
  116. if(lcp(mid,p)>=len)r=(L=mid)-1;else l=mid+1;
  117. }
  118. l=p+1,r=n;
  119. while(l<=r){
  120. mid=(l+r)>>1;
  121. if(lcp(mid,p)>=len)l=(R=mid)+1;else r=mid-1;
  122. }
  123. ll ret=0;
  124. while(S+len-1<=T){
  125. fin=N;
  126. ask(root[S],1,n,L,R);
  127. if(fin+len-1>T)break;
  128. ret+=val[fin+1];
  129. S=fin+len;
  130. }
  131. ans[o]=ret;
  132. }
  133. }
  134. int main(){
  135. read(n),read(m);
  136. for(i=1;i<=n;i++)val[i]=m-i;
  137. scanf("%s%s",a+1,b+1);
  138. for(i=1;i<=n;i++)a[i+n]=b[i];
  139. read(m);
  140. for(i=1;i<=m;i++){
  141. int s,t,l,r;
  142. read(s),read(t),read(l),read(r);
  143. e[i][0]=s,e[i][1]=t,e[i][2]=l,e[i][3]=r;
  144. que[r-l+1].push_back(i);
  145. mx=max(mx,r-l+1);
  146. }
  147. for(i=1;i<=n+n;i++)SA::s[i-1]=a[i];
  148. SA::n=n+n;
  149. SA::init();
  150. for(i=1;i<=n+n+1;i++)q[i]=i;
  151. for(len=1;len<=mx;len++)if(len<=51){
  152. for(i=0;i<S;i++)c[i]=0;
  153. now=n+n-len+1;
  154. for(i=1;i<=now;i++)c[a[i]]++;
  155. for(i=1;i<S;i++)c[i]+=c[i-1];
  156. for(i=now+1;i;i--){
  157. x=q[i]-1;
  158. if(!x)continue;
  159. nq[c[a[x]]--]=x;
  160. }
  161. for(i=0;i<=now;i++)g[i]=0,need[i].clear();
  162. for(i=1;i<=now;i=j){
  163. for(j=i;j<=now;j++){
  164. if(a[nq[i]]!=a[nq[j]])break;
  165. if(id[nq[i]+1]!=id[nq[j]+1])break;
  166. }
  167. en[i]=j-1;
  168. for(x=i;x<j;x++)nid[nq[x]]=i;
  169. for(x=y=i;x<j;x++){
  170. while(y<j&&nq[y]-nq[x]<len)y++;
  171. add(nq[x],y<j?nq[y]:0);
  172. }
  173. }
  174. for(i=1;i<=now;i++)q[i]=nq[i],id[i]=nid[i];
  175. for(i=0;i<que[len].size();i++)small(que[len][i]);
  176. dfs(0);
  177. }else for(i=0;i<que[len].size();i++)SA::solve(que[len][i]);
  178. for(i=1;i<=m;i++)printf("%lld\n",ans[i]);
  179. return 0;
  180. }

  

BZOJ5304 : [Haoi2018]字串覆盖的更多相关文章

  1. 【BZOJ5304】[HAOI2018]字串覆盖(后缀数组,主席树,倍增)

    [BZOJ5304][HAOI2018]字串覆盖(后缀数组,主席树,倍增) 题面 BZOJ 洛谷 题解 贪心的想法是从左往右,能选就选.这个显然是正确的. 题目的数据范围很好的说明了要对于询问分开进行 ...

  2. 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)

    题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...

  3. 「HAOI2018」字串覆盖

    「HAOI2018」字串覆盖 题意: ​ 给你两个字符串,长度都为\(N\),以及一个参数\(K\),有\(M\)个询问,每次给你一个\(B\)串的一个子串,问用这个字串去覆盖\(A\)串一段区间的最 ...

  4. 【LOJ】#2525. 「HAOI2018」字串覆盖

    题解 写后缀树真是一写就好久,然后调好久QAQ 我们把两个串取反拼一起建后缀树,这样的话使得后缀树是正串的后缀树 然后我们把询问挂在每个节点上,每次线段树合并,对于大于50的每次暴力跳着在线段树找,对 ...

  5. 最大公共字串LCS问题(阿里巴巴)

    给定两个串,均由最小字母组成.求这两个串的最大公共字串LCS(Longest Common Substring). 使用动态规划解决. #include <iostream> #inclu ...

  6. 编程:使用递归方式判断某个字串是否回文(Palindrome)

    Answer: import java.util.Scanner; public class Palindrome { private static int len;//全局变量整型数据 privat ...

  7. NOIP2002字串变换[BFS]

    题目描述 已知有两个字串 A$, B$ 及一组字串变换的规则(至多6个规则): A1$ -> B1$ A2$ -> B2$ 规则的含义为:在 A$中的子串 A1$ 可以变换为 B1$.A2 ...

  8. 字串符相关 split() 字串符分隔 substring() 提取字符串 substr()提取指定数目的字符 parseInt() 函数可解析一个字符串,并返回一个整数。

    split() 方法将字符串分割为字符串数组,并返回此数组. stringObject.split(separator,limit) 我们将按照不同的方式来分割字符串: 使用指定符号分割字符串,代码如 ...

  9. mormot 数据集转换为JSON字串

    mormot 数据集转换为JSON字串 unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graph ...

随机推荐

  1. 作业二Wordcount

    1,github地址 https://github.com/dtneverdie/word-count 2,PSP表格 3,解题思路 先从理论上判断应该先将文件内的字符全部读入,然后根据分隔符来进行单 ...

  2. MySql 从SQL文件导入

    1. 运行cmd进入命令模式,进入Mysql安装目录下的bin目录(即mysql.exe所在的目录): cd c:\"program Files"\MySQL\"MySQ ...

  3. word20170105订酒店 hotel reservation有用的词和句子

    有用的词: hotel reservation/booking: 酒店预订 standard room:标准间 suite: 套房 king size bed: 大床房 double bed:双床房 ...

  4. $(document).ready和window.onload的区别

    $(document).ready比window.onload先执行.window.onload只执行一次. $(document).ready和window.onload都是在都是在页面加载完执行的 ...

  5. 【R】资源整理

    1.25本Python电子书 http://python.jobbole.com/29281/ Think Stats Dive Into Python A Byte Of Python Think ...

  6. 012_TCP keepalive 和 http keep-alive

    TCP keepalive概念在使用TCP长连接(复用已建立TCP连接)的场景下,需要对TCP连接进行保活,避免被网关干掉连接.在应用层,可以通过定时发送心跳包的方式实现.而Linux已提供的TCP ...

  7. HTTP协议详解(二)

    接着第一篇学习.... 5 头域(首部) 每个头域由一个域名,冒号(:)和域值三部分组成.域名是大小写无关的,域值前可以添加任何数量的空格符,头域可以被扩展为多行,在每行开始处,使用至少一个空格或制表 ...

  8. spring5.0.2.RELEASE源码环境构建

    Spring5 源码下载注意事项 首先你的JDK 需要升级到1.8 以上.Spring3.0 开始,Spring 源码采用github 托管,不再提供官网下载链接.大家可自行去github 网站下载, ...

  9. javaweb c3p0连接oracle12c

    最近在搞javaweb,在连接池上碰到了一系列的问题,在Junit测试时,oracle12c报错: ORA-28040: 没有匹配的验证协议 百度解决:修改 $ORACLE_HOME/network/ ...

  10. Thymleaf 从某处(不包含某处)开始截取字符串到末尾

    简单描述:数据库存放的是id+name,但是做展示的时候,只需要展示name,不展示id.不管是在前台还是在后台,使用传统的方法截取,也是可以的,但是thymleaf提供了一种截取字符串,可以实现从某 ...