题目

如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串,则我们称该字符串的这种拆

分是优秀的。例如,对于字符串 aabaabaa,如果令 A=aabA=aab,B=aB=a,我们就找到了这个字符串拆分成 AABBA

ABB 的一种方式。一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 A=aA=a,B=baa

B=baa,也可以用 AABBAABB 表示出上述字符串;但是,字符串 abaabaa 就没有优秀的拆分。现在给出一个长度为

nn 的字符串 SS,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串

中连续的一段。以下事项需要注意:出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被

记入答案。在一个拆分中,允许出现 A=BA=B。例如 cccc 存在拆分 A=B=cA=B=c。字符串本身也是它的一个子串。

输入格式

每个输入文件包含多组数据。输入文件的第一行只有一个整数 TT,表示数据的组数。保证 1≤T≤101≤T≤10。接

下来 TT 行,每行包含一个仅由英文小写字母构成的字符串 SS,意义如题所述。

输出格式

输出 TT 行,每行包含一个整数,表示字符串 SS 所有子串的所有拆分中,总共有多少个是优秀的拆分。

输入样例

4

aabbbb

cccccc

aabaabaabaa

bbaabaababaaba

输出样例

3

5

4

7

提示

我们用 S[i,j]S[i,j] 表示字符串 SS 第 ii 个字符到第 jj 个字符的子串(从 11 开始计数)。第一组数据中,

共有 33 个子串存在优秀的拆分:S[1,4]=aabbS[1,4]=aabb,优秀的拆分为 A=aA=a,B=bB=b;S[3,6]=bbbbS[3,6]

=bbbb,优秀的拆分为 A=bA=b,B=bB=b;S[1,6]=aabbbbS[1,6]=aabbbb,优秀的拆分为 A=aA=a,B=bbB=bb。而剩

下的子串不存在优秀的拆分,所以第一组数据的答案是 33。第二组数据中,有两类,总共 44 个子串存在优秀的

拆分:对于子串 S[1,4]=S[2,5]=S[3,6]=ccccS[1,4]=S[2,5]=S[3,6]=cccc,它们优秀的拆分相同,均为 A=cA=c,

B=cB=c,但由于这些子串位置不同,因此要计算 33 次;对于子串 S[1,6]=ccccccS[1,6]=cccccc,它优秀的拆分

有 22 种:A=cA=c,B=ccB=cc 和 A=ccA=cc,B=cB=c,它们是相同子串的不同拆分,也都要计入答案。所以第二组

数据的答案是 3+2=53+2=5。第三组数据中,S[1,8]S[1,8] 和 S[4,11]S[4,11] 各有 22 种优秀的拆分,其中 S[1

,8]S[1,8] 是问题描述中的例子,所以答案是 2+2=42+2=4。第四组数据中,S[1,4]S[1,4],S[6,11]S[6,11],S[7

,12]S[7,12],S[2,11]S[2,11],S[1,8]S[1,8] 各有 11 种优秀的拆分,S[3,14]S[3,14] 有 22 种优秀的拆分,

所以答案是 5+2=75+2=7。

题解

我们设\(f[i]\)为以\(i\)为结尾的\(AA\)串的数量

设\(g[i]\)为以\(i\)开头的\(AA\)串的数量

那么

\[ans = \sum\limits_{i = 2}^{n - 2} f[i] * g[i + 1]
\]

所以我们只要找出所有\(AA\)串即可

根据后缀数组的套路,为找出所有\(AA\)串,我们枚举\(A\)的长度\(L\),然后每隔\(L\)设一个监测点,如图:



其中圈起来的就是中间那个监测点所管辖的\(len = 3\)的子串

如此,如果存在长度为\(2 * L\)的\(AA\)串,那么相邻的\(A\)中一定有且仅有一个相邻的监测点

我们就枚举相邻的两个监测点,比较它们的lcp和往前的lcp大小,就可以确定它们管辖的串那些可以匹配

具体对正串反串分别求一次SA【或者并在一起求】,做到\(O(1)\)询问lcp

然后用一个差分数组维护\(f[i]\)和\(g[i]\)

最后统计答案就做完了

时间复杂度\(O(nlogn + \sum\limits_{L = 1}^{n} \frac{n}{L}) = O(nlogn + n * \sum\limits_{L = 1}^{n} \frac{1}{L}) = O(nlogn)\)

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cmath>
  4. #include<cstring>
  5. #include<algorithm>
  6. #define LL long long int
  7. #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
  8. #define REP(i,n) for (int i = 1; i <= (n); i++)
  9. #define cls(s) memset(s,0,sizeof(s))
  10. using namespace std;
  11. const int maxn = 100005,maxm = 100005,INF = 1000000000;
  12. inline int read(){
  13. int out = 0,flag = 1; char c = getchar();
  14. while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
  15. while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
  16. return out * flag;
  17. }
  18. char s[maxn];
  19. int N,n,m,sa[maxn],rank[maxn],height[maxn],t1[maxn],t2[maxn],bac[maxn];
  20. int mn[maxn][18],bin[30],Log[maxn];
  21. void getsa(){
  22. int *x = t1,*y = t2; m = 1000;
  23. for (int i = 0; i <= m; i++) bac[i] = 0;
  24. for (int i = 1; i <= n; i++) bac[x[i] = s[i]]++;
  25. for (int i = 1; i <= m; i++) bac[i] += bac[i - 1];
  26. for (int i = n; i; i--) sa[bac[x[i]]--] = i;
  27. for (int k = 1; k <= n; k <<= 1){
  28. int p = 0;
  29. for (int i = n - k + 1; i <= n; i++) y[++p] = i;
  30. for (int i = 1; i <= n; i++) if (sa[i] - k > 0) y[++p] = sa[i] - k;
  31. for (int i = 0; i <= m; i++) bac[i] = 0;
  32. for (int i = 1; i <= n; i++) bac[x[y[i]]]++;
  33. for (int i = 1; i <= m; i++) bac[i] += bac[i - 1];
  34. for (int i = n; i; i--) sa[bac[x[y[i]]]--] = y[i];
  35. swap(x,y);
  36. x[sa[1]] = p = 1;
  37. for (int i = 2; i <= n; i++)
  38. x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? p : ++p);
  39. if (p >= n) break;
  40. m = p;
  41. }
  42. for (int i = 1; i <= n; i++) rank[sa[i]] = i;
  43. for (int i = 1,k = 0; i <= n; i++){
  44. if (k) k--;
  45. int j = sa[rank[i] - 1];
  46. while (s[i + k] == s[j + k]) k++;
  47. height[rank[i]] = k;
  48. }
  49. for (int i = 1; i <= n; i++) mn[i][0] = height[i];
  50. REP(j,17) REP(i,n){
  51. if (i + bin[j] - 1 > n) break;
  52. mn[i][j] = min(mn[i][j - 1],mn[i + bin[j - 1]][j - 1]);
  53. }
  54. }
  55. int lcp(int a,int b){
  56. int l = rank[a],r = rank[b];
  57. if (l > r) swap(l,r); l++;
  58. int t = Log[r - l + 1];
  59. return min(mn[l][t],mn[r - bin[t] + 1][t]);
  60. }
  61. int pre_lcp(int a,int b){
  62. int l = rank[N - a + 1],r = rank[N - b + 1];
  63. if (l > r) swap(l,r); l++;
  64. int t = Log[r - l + 1];
  65. return min(mn[l][t],mn[r - bin[t] + 1][t]);
  66. }
  67. LL f[maxn],g[maxn];
  68. void solve(){
  69. memset(f,0,sizeof(f));
  70. memset(g,0,sizeof(g));
  71. for (int L = 1; L <= (n >> 1); L++){
  72. for (int a = L,b = a + L,l,r,lenl,lenr,len; b <= n; a += L,b += L){
  73. lenl = min(pre_lcp(a,b),L);
  74. lenr = min(lcp(a,b),L);
  75. len = lenl + lenr - 1;
  76. l = a - lenl + 1; r = l + len - L;
  77. if (l <= r) g[l]++,g[r + 1]--;
  78. l = b - lenl + L; r = l + len - L;
  79. if (l <= r) f[l]++,f[r + 1]--;
  80. }
  81. }
  82. REP(i,n) g[i] += g[i - 1],f[i] += f[i - 1];
  83. //REP(i,n) printf("%lld",f[i]); puts("");
  84. //REP(i,n) printf("%lld",g[i]); puts("");
  85. LL ans = 0;
  86. for (int i = 2; i < n - 1; i++){
  87. ans += f[i] * g[i + 1];
  88. }
  89. printf("%lld\n",ans);
  90. }
  91. int main(){
  92. bin[0] = 1; for (int i = 1; i <= 25; i++) bin[i] = bin[i - 1] << 1;
  93. Log[0] = -1; for (int i = 1; i < maxn; i++) Log[i] = Log[i >> 1] + 1;
  94. int T = read();
  95. while (T--){
  96. cls(s); cls(t1); cls(t2);
  97. scanf("%s",s + 1); n = strlen(s + 1);
  98. s[n + 1] = '#';
  99. for (int i = 1; i <= n; i++) s[n + 1 + i] = s[n - i + 1];
  100. N = n = n << 1 | 1;
  101. getsa();
  102. n >>= 1;
  103. solve();
  104. }
  105. return 0;
  106. }

BZOJ4650 [NOI2016]优秀的拆分 【后缀数组】的更多相关文章

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

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

  2. BZOJ.4650.[NOI2016]优秀的拆分(后缀数组 思路)

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

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

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

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

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

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

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

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

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

  7. bzoj千题计划317:bzoj4650: [Noi2016]优秀的拆分(后缀数组+差分)

    https://www.lydsy.com/JudgeOnline/problem.php?id=4650 如果能够预处理出 suf[i] 以i结尾的形式为AA的子串个数 pre[i] 以i开头的形式 ...

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

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

  9. UOJ#219/BZOJ4650 [NOI2016]优秀的拆分 字符串 SA ST表

    原文链接http://www.cnblogs.com/zhouzhendong/p/9025092.html 题目传送门 - UOJ#219 (推荐,题面清晰) 题目传送门 - BZOJ4650 题意 ...

随机推荐

  1. Oracle 数据库、实例、表空间、用户、数据库对象

    Oracle是一种数据库管理系统,是一种关系型的数据库管理系统.通常情况了我们称的“数据库”,包含了物理数据.数据库管理系统.内存.操作系统进程的组合体,就是指这里所说的数据库管理系统. 完整的Ora ...

  2. ios swift 里面关于变量 常量 可选类型 控制流的一些心得

    //swift 里面没有头文件和实现文件.只有一个.swift文件 //swift 里面没有main的概念,程序从main.swift开始执行 //swift 每一条执行语句可以不用分号结束,多条语句 ...

  3. linux网络编程之断点传输文件

    以下载链接"http://www.boa.org/boa-0.94.13.tar.gz"为例: 断点续传实验大概步骤: ===================== 1,使用geth ...

  4. 项目实战2.3-Nginx的“远方表哥”—Tengine

    本文收录在Linux运维企业架构实战系列 今天想起当初研究nginx反向代理负载均衡时,nginx自身的upstream后端配置用着非常不舒服: 当时使用的淘宝基于nginx二次开发的Tengine, ...

  5. 重置 nexus3 admin 密码

    2 简单 3 重构,变化很大 如何处理nexus3忘记admin密码 - CSDN博客 https://blog.csdn.net/tianya6607/article/details/5330562 ...

  6. c#用object将datatable快速填充excel后下载表格后打不开的问题

    最近在用c#的asp.net,需要批量导出数据.原本用的是stringbuilder逐个填充,但是只能做到html强制格式转换为xls,这不是真正的excel表格,所以在网上找了datatable快速 ...

  7. Python中的bytes

    bytes_lst = [ ('创建bytes',), ('bytes可哈希',), ('编码与解码',), ('常见编码类型',), ('ord() 与 chr()',), ] 创建bytes &g ...

  8. Codeforces Round #456 (Div. 2) B. New Year's Eve

    传送门:http://codeforces.com/contest/912/problem/B B. New Year's Eve time limit per test1 second memory ...

  9. SharedPreferences使用(通过键值保存数据)

    保存数据到SharedPreferences中 要想使用SharedPreferences来存储数据, 首先需要获取到SharedPreferences对象. Android中主要提供了三种方法用于得 ...

  10. Redis实现之AOF持久化

    AOF持久化 除了RDB持久化功能之外,Redis还提供了AOF(Append Only File)持久化功能,与RDB持久化通过保存数据库中的键值对来记录数据库状态不同,AOF持久化是通过保存Red ...