如果一个字符串可以被拆分为\(AABB\)的形式,其中$A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的。

例如,对于字符串\(aabaabaa\),如果令\(A=aab\),\(B=a\),我们就找到了这个字符串拆分成\(AABB\)的一种方式。

一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令\(A=a\),\(B=baa\),也可以用 AABB表示出上述字符串;但是,字符串\(abaabaa\)就没有优秀的拆分。

现在给出一个长度为\(n\)的字符串\(S\),我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。

以下事项需要注意:

  • 出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。

  • 在一个拆分中,允许出现\(A=B\)。例如\(cccc\)存在拆分\(A=B=c\)。

  • 字符串本身也是它的一个子串。

对于\(AABB\),我们完全可以只考虑\(AA\),这样令\(f[i]\)表示以i结尾的\(AA\)数量,\(g[i]\)表示以\(i\)开头的\(AA\)数量,那么显然就是\(sigma(f[i]g[i+1])\)。

对于\(AA\)怎么求,大体的思路和URAL1297:Palindrome求回文串是一样的,就是通过比较后缀的公共前缀来得到AA的长度,进而求出这段区间内\(f[i]g[i]\)的值。

但是这样显然是\(O(n^{2})\)的。

我们用分块的思想,枚举\(l\),将字符串分成\(l\)大小的块,则长度为\(l\)的\(AA\)一定最少跨过两个块,于是对于块边界点,求一次公共前缀和后缀,拼在一起就是我们所要的答案,复杂度调和级数\(O(n×log_{2}^{n})\)。

注意,为了让复杂度正确,我们对区间的\(f\)和\(g\)差分。

代码:

  1. #include<cstdio>
  2. #include<cmath>
  3. #include<iostream>
  4. #include<vector>
  5. #include<cstring>
  6. #include<algorithm>
  7. #include<cctype>
  8. using namespace std;
  9. typedef long long ll;
  10. const int N=2e6+10;
  11. char s[N];
  12. int n,m,rk[N],height[N],sa[N],w[N],cas,dp[N][21],lg[N];
  13. int f[N],g[N];
  14. inline int qpow(int a)
  15. {
  16. return 1<<a;
  17. }
  18. inline bool pan(int *x,int i,int j,int k)
  19. {
  20. int ti=i+k<n?x[i+k]:-1;
  21. int tj=j+k<n?x[j+k]:-1;
  22. return ti==tj&&x[i]==x[j];
  23. }
  24. void SA_init()
  25. {
  26. int *x=rk,*y=height,r=256;
  27. for(int i=0; i<r; i++)w[i]=0;
  28. for(int i=0; i<n; i++)w[s[i]]++;
  29. for(int i=1; i<r; i++)w[i]+=w[i-1];
  30. for(int i=n-1; i>=0; i--)sa[--w[s[i]]]=i;
  31. r=1;
  32. x[sa[0]]=0;
  33. for(int i=1; i<n; i++)
  34. x[sa[i]]=s[sa[i]]==s[sa[i-1]]?r-1:r++;
  35. for(int k=1; r<n; k<<=1)
  36. {
  37. int yn=0;
  38. for(int i=n-k; i<n; i++)y[yn++]=i;
  39. for(int i=0; i<n; i++)
  40. if(sa[i]>=k)y[yn++]=sa[i]-k;
  41. for(int i=0; i<r; i++)w[i]=0;
  42. for(int i=0; i<n; i++)w[x[y[i]]]++;
  43. for(int i=1; i<r; i++)w[i]+=w[i-1];
  44. for(int i=n-1; i>=0; i--)sa[--w[x[y[i]]]]=y[i];
  45. swap(x,y);
  46. r=1;
  47. x[sa[0]]=0;
  48. for(int i=1; i<n; i++)
  49. x[sa[i]]=pan(y,sa[i],sa[i-1],k)?r-1:r++;
  50. }
  51. }
  52. inline void height_init()
  53. {
  54. int i,j,k=0;
  55. for(int i=1; i<=n; i++)rk[sa[i]]=i;
  56. for(int i=0; i<n; i++)
  57. {
  58. if(k)k--;
  59. j=sa[rk[i]-1];
  60. while(s[i+k]==s[j+k])k++;
  61. height[rk[i]]=k;
  62. }
  63. }
  64. void st_init()
  65. {
  66. for(int i=1; i<=n; i++)
  67. {
  68. dp[i-1][0]=height[i];
  69. lg[i]=lg[i-1];
  70. if((1<<lg[i]+1)==i)lg[i]++;
  71. }
  72. for(int j=1; j<=lg[n]; j++)
  73. {
  74. for(int i=0; i<n; i++)
  75. {
  76. if(i+qpow(j)-1>=n)break;
  77. dp[i][j]=min(dp[i][j-1],dp[i+qpow(j-1)][j-1]);
  78. }
  79. }
  80. }
  81. int lcp(int a,int b)
  82. {
  83. int l=rk[a],r=rk[b];
  84. if(r<l)swap(l,r);
  85. l--;
  86. r--;
  87. if(r<0)return 0;
  88. l++;
  89. int len=r-l+1;
  90. int k=lg[len];
  91. int h=qpow(k);
  92. return min(dp[l][k],dp[r-h+1][k]);
  93. }
  94. int main()
  95. {
  96. scanf("%d",&cas);
  97. while(cas--)
  98. {
  99. memset(f,0,sizeof(f));
  100. memset(g,0,sizeof(g));
  101. cin>>s;
  102. m=strlen(s),n=2*m+1;
  103. s[m]='$';
  104. for(int i=m+1; i<n; i++)
  105. {
  106. s[i]=s[n-i-1];
  107. }
  108. s[n++]=0;
  109. SA_init();
  110. n--;
  111. height_init();
  112. st_init();
  113. for(int l=1; l<=m/2; l++)
  114. {
  115. for(int i=0,j=l; j<m; i+=l,j+=l)
  116. {
  117. int p=min(l,lcp(i,j));
  118. int s=min(l,lcp(n-i-1,n-j-1));
  119. if(p+s-1>=l)
  120. {
  121. f[j-s+l]++;
  122. f[j+p]--;
  123. g[i-s+1]++;
  124. g[i+p-l+1]--;
  125. }
  126. }
  127. }
  128. ll ans=0;
  129. for(int i=1; i<m; i++)
  130. {
  131. f[i]+=f[i-1];
  132. g[i]+=g[i-1];
  133. }
  134. for(int i=0; i<m-1; i++)
  135. {
  136. ans+=(ll)f[i]*g[i+1];
  137. }
  138. printf("%lld\n",ans);
  139. }
  140. return 0;
  141. }

『题解』[NOI2016]优秀的拆分的更多相关文章

  1. 题解-NOI2016 优秀的拆分

    NOI2016 优秀的拆分 \(T\) 组测试数据.求字符串 \(s\) 的所有子串拆成 \(AABB\) 形式的方案总和. 数据范围:\(1\le T\le 10\),\(1\le n\le 3\c ...

  2. [NOI2016]优秀的拆分&&BZOJ2119股市的预测

    [NOI2016]优秀的拆分 https://www.lydsy.com/JudgeOnline/problem.php?id=4650 题解 如果我们能够统计出一个数组a,一个数组b,a[i]表示以 ...

  3. 【BZOJ4560】[NOI2016]优秀的拆分

    [BZOJ4560][NOI2016]优秀的拆分 题面 bzoj 洛谷 题解 考虑一个形如\(AABB\)的串是由两个形如\(AA\)的串拼起来的 那么我们设 \(f[i]\):以位置\(i\)为结尾 ...

  4. [UOJ#219][BZOJ4650][Noi2016]优秀的拆分

    [UOJ#219][BZOJ4650][Noi2016]优秀的拆分 试题描述 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 A 和 B 是任意非空字符串,则我们称该字符串的这种拆分是优秀 ...

  5. [NOI2016]优秀的拆分(SA数组)

    [NOI2016]优秀的拆分 题目描述 如果一个字符串可以被拆分为 \(AABB\) 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 \(aabaaba ...

  6. luogu1117 [NOI2016]优秀的拆分

    luogu1117 [NOI2016]优秀的拆分 https://www.luogu.org/problemnew/show/P1117 后缀数组我忘了. 此题哈希可解决95分(= =) 设\(l_i ...

  7. 『题解』洛谷P1063 能量项链

    原文地址 Problem Portal Portal1:Luogu Portal2:LibreOJ Portal3:Vijos Description 在\(Mars\)星球上,每个\(Mars\)人 ...

  8. BZOJ4650:[NOI2016]优秀的拆分——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4650 https://www.luogu.org/problemnew/show/P1117 如果 ...

  9. [NOI2016]优秀的拆分 后缀数组

    题面:洛谷 题解: 因为对于原串的每个长度不一定等于len的拆分而言,如果合法,它将只会被对应的子串统计贡献. 所以子串这个限制相当于是没有的. 所以我们只需要对于每个位置i求出f[i]表示以i为开头 ...

随机推荐

  1. 在线影视平台人人影视 v3.2.1 绿色便携版

    人人影视是一款可以方便观看美剧和国外大片的视频播放软件,支持在线观看.网盘转存.离线缓存.所有客户端离线下载均加密传输,不用担心任何安全问题.全程加密的 P2P 传输,让热门资源下载更快,海外党不再惧 ...

  2. 【NOIP模拟赛】小奇的矩阵

    [题目背景] 小奇总是在数学课上思考奇怪的问题. [问题描述] 给定一个n*m的矩阵,矩阵中的每个元素aij为正整数. 接下来规定 1.合法的路径初始从矩阵左上角出发,每次只能向右或向下走,终点为右下 ...

  3. c语言中double类型数据的输入和输出

    double a;scanf("%f",&a);   //应用scanf("%lf",&a);执行上面语句时,发现double类型的输入不能使用 ...

  4. 告诉你如何回答"线上CPU100%排查"面试问题

    不知道在大家面试中,有没有遇到这个问题: 生产服务器上部署了几个java程序,突然出现了CPU100%的异常告警,你如何定位出问题呢? 这个问题分为两版回答!高调版对不起,我是做研发的,这个问题在生产 ...

  5. Redis 3.0中文版学习(一)

    网址:http://wiki.jikexueyuan.com/project/redis-guide/entry-to-master-a.html http://www.yiibai.com/redi ...

  6. mysql 数据分析如何实现日报、周报、月报和年报?

    以天为统计周期,是常见需求.周报.月报更是常见需求.长周期项目,甚至有年报需求.我已经掌握了mysql中按天统计,如何实现按年.按月.按周统计呢? 1.已掌握的技能:按天统计 实现以天为统计周期很简单 ...

  7. Unity - Raycast 射线检测

    本文简要分析了Unity中射线检测的基本原理及用法,包括: Ray 射线 RaycastHit 光线投射碰撞信息 Raycast 光线投射 SphereCast 球体投射 OverlapSphere ...

  8. std::is_same

    两个一样的类型会返回true bool isInt = std::is_same<int, int>::value; //为true std::cout << std::is_ ...

  9. django报错问题解决

    注意以下修改文件均是修改虚拟python环境中的文件 1.执行(venv) E:\myproj\autotest>python manage.py makemigrations报错: 解决办法: ...

  10. 推荐一款简单易用线上引流测试工具:GoReplay

    一. 引流测试产生背景 日常大部分的测试工作都是在测试环境下,通过模拟用户的行为来对系统进行验证,包括功能以及性能.在这个过程中,你可能会遇到以下问题: 用户访问行为比较复杂,模拟很难和用户行为一致, ...