BZOJ

洛谷

令\(st[i]\)表示以\(i\)为开头有多少个\(AA\)这样的子串,\(ed[i]\)表示以\(i\)结尾有多少个\(AA\)这样的子串。那么\(Ans=\sum_{i=1}^{n-1}ed[i]*st[i+1]\)。

考虑如何求\(st[i],ed[i]\)。暴力的话可以枚举\(i\),然后哈希判一下。这样\(O(n^2)\)就有\(95\)分了。。

正解是,枚举长度\(len\),判断每个位置是否存在长为\(2*len\)的\(AA\)这样的子串。

每隔\(len\)的距离放一个关键点,这样一个长度为\(2*len\)的串一定会经过两个相邻的关键点。

考虑枚举两个相邻的关键点,即令\(i=k*len,\ j=i+len\)。再令\(x\)表示\(i,j\)所代表的前缀的最长公共后缀(与\(len\)取\(\min\)),\(y\)表示\(i,j\)所代表的后缀的最长公共前缀(与\(len\)取\(\min\))。

(不想画图了,注意别看错,可以拿个串比如aabaabab试一下)

当\(x+y-1<len\)时,因为中间没有相同的部分所以找不到一个经过\(i,j\)长为\(2*len\)的\(AA\)串。

当\(x+y-1\geq len\)时,我们发现因为\(i,j\)是两个相距为\(len\)的点,我们取\(i-x+len,\ j-x+len\),这两个点之间能形成长\(2*len\)的\(AA\)子串。同时将两个点不断向右移动,直到\(i+y-1,\ j+y-1\),都能形成一个\(AA\)子串。

也就是当\(p\)取\([j-x+len,\ j+y-1]\)中的某个位置时,都能得到以\(p\)为结尾的长为\(2*len\)的\(AA\)串。同理当\(p\)在\([i-x+1,\ i+y-len]\)中时,也都能得到以\(p\)开头的长为\(2*len\)的\(AA\)串。

所以就是区间加一,差分一下就可以了。

只是枚举\(len\),然后每隔\(len\)放一个点,统计相邻两点间的贡献。所以复杂度还是\(O(n\log n)\)。

  1. //5892kb 784ms
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. typedef long long LL;
  6. const int N=3e4+5;
  7. int Log[N];
  8. struct Suffix_Array
  9. {
  10. int tm[N],sa[N],sa2[N],rk[N],ht[N],st[N][15];
  11. inline void Init_ST(const int n)
  12. {
  13. for(int i=1; i<=n; ++i) st[i][0]=ht[i];
  14. for(int j=1; j<=Log[n]; ++j)
  15. for(int t=1<<j-1,i=n-t; i; --i)
  16. st[i][j]=std::min(st[i][j-1],st[i+t][j-1]);
  17. }
  18. inline int LCP(int l,int r)
  19. {
  20. l=rk[l], r=rk[r]; if(l>r) std::swap(l,r);
  21. ++l;
  22. int k=Log[r-l+1];
  23. return std::min(st[l][k],st[r-(1<<k)+1][k]);
  24. }
  25. void Build(char *s,const int n)
  26. {
  27. memset(rk,0,sizeof rk);
  28. memset(sa2,0,sizeof sa2);//要清空...! 因为下面比较懒得加<=n了。
  29. int m=26,*x=rk,*y=sa2;
  30. for(int i=0; i<=m; ++i) tm[i]=0;
  31. for(int i=1; i<=n; ++i) ++tm[x[i]=s[i]-'a'+1];
  32. for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
  33. for(int i=n; i; --i) sa[tm[x[i]]--]=i;
  34. for(int k=1,p=0; k<n; k<<=1,m=p,p=0)
  35. {
  36. for(int i=n-k+1; i<=n; ++i) y[++p]=i;
  37. for(int i=1; i<=n; ++i) if(sa[i]>k) y[++p]=sa[i]-k;
  38. for(int i=0; i<=m; ++i) tm[i]=0;
  39. for(int i=1; i<=n; ++i) ++tm[x[i]];
  40. for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
  41. for(int i=n; i; --i) sa[tm[x[y[i]]]--]=y[i];
  42. std::swap(x,y), x[sa[1]]=p=1;
  43. for(int i=2; i<=n; ++i)
  44. x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p:++p;//because of this
  45. if(p>=n) break;
  46. }
  47. for(int i=1; i<=n; ++i) rk[sa[i]]=i;
  48. ht[1]=0;
  49. for(int i=1,k=0,p; i<=n; ++i)
  50. {
  51. if(rk[i]==1) continue;
  52. if(k) --k;
  53. p=sa[rk[i]-1];
  54. while(i+k<=n && p+k<=n && s[i+k]==s[p+k]) ++k;
  55. ht[rk[i]]=k;
  56. }
  57. Init_ST(n);
  58. }
  59. }sa1,sa2;
  60. inline void Init_Log(const int n)
  61. {
  62. for(int i=2; i<=n; ++i) Log[i]=Log[i>>1]+1;
  63. }
  64. void Solve()
  65. {
  66. static int st[N],ed[N];
  67. static char s[N];
  68. scanf("%s",s+1); const int n=strlen(s+1);
  69. sa1.Build(s,n), std::reverse(s+1,s+1+n), sa2.Build(s,n);
  70. memset(st,0,n+1<<2), memset(ed,0,n+1<<2);
  71. for(int len=1,lim=n>>1; len<=lim; ++len)
  72. for(int i=len,j=len<<1; j<=n; i=j,j+=len)
  73. {
  74. int x=std::min(len,sa2.LCP(n-i+1,n-j+1)),y=std::min(len,sa1.LCP(i,j));
  75. if(x+y-1>=len)
  76. ++st[i-x+1], --st[i+y-len+1], ++ed[j-x+len], --ed[j+y];
  77. }
  78. LL ans=0;
  79. for(int i=1; i<n; ++i) st[i+1]+=st[i], ed[i+1]+=ed[i], ans+=1ll*ed[i]*st[i+1];
  80. printf("%lld\n",ans);
  81. }
  82. int main()
  83. {
  84. Init_Log(30000);
  85. int T; scanf("%d",&T);
  86. while(T--) Solve();
  87. return 0;
  88. }

BZOJ.4650.[NOI2016]优秀的拆分(后缀数组 思路)的更多相关文章

  1. BZOJ 4650 [Noi2016]优秀的拆分 ——后缀数组

    我们只需要统计在某一个点开始的形如$AA$字符串个数,和结束的个数相乘求和. 首先枚举循环节的长度L.即$\mid (A) \mid=L$ 然后肯定会经过s[i]和[i+L]至少两个点. 然后我们可以 ...

  2. BZOJ 4650 [Noi2016]优秀的拆分:后缀数组

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4650 题意: 给你一个字符串s,问你s及其子串中,将它们拆分成"AABB&quo ...

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

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

  4. [BZOJ]4650: [Noi2016]优秀的拆分

    Time Limit: 30 Sec  Memory Limit: 512 MB Description 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串, ...

  5. 【刷题】BZOJ 4650 [Noi2016]优秀的拆分

    Description 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串,则我们称该字符串的这种拆分是优秀的.例如,对于字符串 aabaabaa,如果令 A ...

  6. UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组、ST表)

    连NOI Day1T1都不会做...看了题解都写不出来还要抄Claris的代码.. 题目链接: (luogu)https://www.luogu.org/problemnew/show/P1117 ( ...

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

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

  8. UOJ#219. 【NOI2016】优秀的拆分 [后缀数组 ST表]

    #219. [NOI2016]优秀的拆分 题意:求有多少AABB样子的子串,拆分不同的同一个子串算多个 一开始一直想直接求,并不方便 然后看了一眼Claris的题解的第一行就有思路了 如果分开,求\( ...

  9. NOI 2016 优秀的拆分 (后缀数组+差分)

    题目大意:给你一个字符串,求所有子串的所有优秀拆分总和,优秀的拆分被定义为一个字符串可以被拆分成4个子串,形如$AABB$,其中$AA$相同,$BB$相同,$AB$也可以相同 作为一道国赛题,95分竟 ...

随机推荐

  1. 4.8cf自训

    发现cf以前的好题真的很多.. cf 730j 01背包变形 感觉很好的题 /* 先处理出最少需要t个瓶子 dp[i][j][k]前i个取k个,容量为j时的水的体积 滚动数组搞一下 本题的状态转移必须 ...

  2. base64解密

    问题 : base64解密 时间限制: 1 Sec  内存限制: 128 MB 题目描述 Base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之后在6位的前面补两个 ...

  3. Axure-----三级下拉菜单的具体实现过程

    ********三级下拉菜单的动画效果:********** 1.选中三级菜单将其转换为动态面板,命名为treePanel,并隐藏. 2.选中二级菜单添加交互效果:[切换可见性],勾选treePane ...

  4. 饮冰三年-人工智能-linux-04 vim编辑器

    vim的三种模式:命令行模式.编辑模式.扩展模式 1:命令行模式下常见的操作 删除 a):dd 删除光标所在当前行 b):ndd   删除光标所在当前行后的n行 复制 c):yy 复制光标所在当前行 ...

  5. loss函数学习笔记

    一直对机器学习里的loss函数不太懂,这里做点笔记. 符号表示的含义,主要根据Andrew Ng的课程来的,\(m\)个样本,第\(i\)个样本为\(\vec x^{(i)}\),对应ground t ...

  6. bzoj3992

    题解: 求模素数 p 原根的方法:对 p-1 进行素因子分解,记pi为p-1的第i个因子,若恒有a^((p-1)/pi) mod p ≠ 1  成立,则 a 就是 p 的原根.(对于合数求原根,只需把 ...

  7. 【译】理解JavaScript闭包——新手指南

    闭包是JavaScript中一个基本的概念,每个JavaScript开发者都应该知道和理解的.然而,很多新手JavaScript开发者对这个概念还是很困惑的. 正确理解闭包可以帮助你写出更好.更高效. ...

  8. 【前端基础系列】slice方法将类数组转换数组实现原理

    问题描述 在日常编码中会遇到将类数组对象转换为数组的问题,其中常用到的一种方式使用Array.prototype.slice()方法. 类数组对象 所谓的类数组对象,JavaScript对它们定义为: ...

  9. Web前端开发:Sublime Text 常用插件

    在安装这些插件之前,确保你已经安装了Package Control.   安装Package Control方法:   通过菜单栏View->Show Console 或者快捷键Ctrl+` 打 ...

  10. python基础——高级特性

    1.切片  切片: >>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack'] >>> L[:3] ['Michael ...