考虑分别计算每个位置作为AA的末尾或者BB的开头的个数 最后乘一乘就是答案

据说是套路的计算AA的方法:

首先枚举A的长度L,然后每L个字符当做一个关键点,这样的话,一个AA包含且只包含相邻两个关键点(记为a,b),而且满足lcp(a,b)+lcs(a,b)-1>=L 手画一下就能看出来

于是SA搞lcp 倒过来再SA搞lcs 最后差分一下统计答案即可

  1. #include<bits/stdc++.h>
  2. #define pa pair<int,int>
  3. #define CLR(a,x) memset(a,x,sizeof(a))
  4. #define MP make_pair
  5. using namespace std;
  6. typedef long long ll;
  7. const int maxn=3e4+;
  8.  
  9. inline char gc(){
  10. return getchar();
  11. static const int maxs=<<;static char buf[maxs],*p1=buf,*p2=buf;
  12. return p1==p2&&(p2=(p1=buf)+fread(buf,,maxs,stdin),p1==p2)?EOF:*p1++;
  13. }
  14. inline ll rd(){
  15. ll x=;char c=gc();bool neg=;
  16. while(c<''||c>''){if(c=='-') neg=;c=gc();}
  17. while(c>=''&&c<='') x=(x<<)+(x<<)+c-'',c=gc();
  18. return neg?(~x+):x;
  19. }
  20.  
  21. char str[maxn];
  22. int lg2[maxn],N;
  23.  
  24. struct SA{
  25. int cnt[maxn*],tmp[maxn*],rnk[maxn*],sa[maxn*],hei[maxn];
  26. int st[maxn][],n;
  27.  
  28. inline void build(char *s){
  29. int i,j=,k,m=;
  30. CLR(cnt,);CLR(rnk,);
  31. for(i=;i<=n;i++) cnt[s[i]-'a']=;
  32. for(i=;i<=m;i++) cnt[i]+=cnt[i-];
  33. for(i=;i<=n;i++) rnk[i]=cnt[s[i]-'a'];
  34. for(k=;j!=n;k<<=){
  35. CLR(cnt,);
  36. for(i=;i<=n;i++) cnt[rnk[i+k]]++;
  37. for(i=;i<=m;i++) cnt[i]+=cnt[i-];
  38. for(i=n;i;i--) tmp[cnt[rnk[i+k]]--]=i;
  39. CLR(cnt,);
  40. for(i=;i<=n;i++) cnt[rnk[i]]++;
  41. for(i=;i<=m;i++) cnt[i]+=cnt[i-];
  42. for(i=n;i;i--) sa[cnt[rnk[tmp[i]]]--]=tmp[i];
  43. memcpy(tmp,rnk,sizeof(rnk));
  44. rnk[sa[]]=j=;
  45. for(i=;i<=n;i++){
  46. if(tmp[sa[i]]!=tmp[sa[i-]]||tmp[sa[i]+k]!=tmp[sa[i-]+k]) j++;
  47. rnk[sa[i]]=j;
  48. }m=j;
  49. }
  50.  
  51. hei[]=;
  52. for(i=,j=;i<=n;i++){
  53. if(rnk[i]==) continue;
  54. if(j) j--;
  55. int x=sa[rnk[i]-];
  56. while(i+j<=n&&x+j<=n&&s[i+j]==s[x+j]) j++;
  57. hei[rnk[i]]=j;
  58. }
  59. // for(i=1;i<=n;i++) printf("hei:%d %d ; rnk:%d ; sa:%d \n",i,hei[i],rnk[i],sa[i]);
  60. for(i=n;i;i--){
  61. st[i][]=hei[i];
  62. for(j=;i+(<<j)-<=n;j++){
  63. st[i][j]=min(st[i][j-],st[i+(<<(j-))][j-]);
  64. }
  65. }
  66. }
  67.  
  68. inline int query(int x,int y){ //x,y:pos
  69. if(x==y) return n-y+;
  70. int l=min(rnk[x],rnk[y])+,r=max(rnk[x],rnk[y]);
  71. // printf("~%d %d\n",l,r);
  72. int k=lg2[r-l+];
  73. return min(st[l][k],st[r-(<<k)+][k]);
  74. }
  75. }fw,bw;
  76.  
  77. int end[maxn],beg[maxn];
  78.  
  79. int main(){
  80. //freopen("","r",stdin);
  81. int i,j,k;
  82. for(i=;i<=;i++) lg2[i]=lg2[i>>]+;
  83. for(int T=rd();T;T--){
  84. scanf("%s",str+);N=strlen(str+);
  85. fw.n=bw.n=N;
  86. fw.build(str);
  87. reverse(str+,str+N+);
  88. bw.build(str);
  89. CLR(end,);CLR(beg,);
  90. for(int l=;l<=N;l++){
  91. i=l+;
  92. for(;i<=N;i+=l){
  93. int lcp=min(l,fw.query(i-l,i)),lcs=min(l,bw.query(N-i+,N-(i-l)+));
  94. // printf("!%d %d %d %d\n",i,i-l,lcp,lcs);
  95. if(lcp+lcs->=l){
  96. end[i+l-lcs]++,end[i+lcp]--;
  97. // printf("add A:%d %d\n",i+l-lcs,i+lcp-1);
  98. beg[i-l-lcs+]++,beg[i-*l+lcp+]--;
  99. // printf("add B:%d %d\n",i-l-lcs+1,i-2*l+lcp);
  100. }
  101. }
  102. }
  103. for(i=;i<=N;i++) end[i]+=end[i-],beg[i]+=beg[i-];
  104. ll ans=;
  105. for(i=;i<N;i++){
  106. ans+=end[i]*beg[i+];
  107. }
  108. printf("%lld\n",ans);
  109. }
  110. return ;
  111. }

luogu1117 优秀的拆分 (后缀数组)的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  8. 【BZOJ4650】【NOI2016】优秀的拆分(后缀数组)

    [BZOJ4650][NOI2016]优秀的拆分(后缀数组) 题面 BZOJ Uoj 题解 如果我们知道以某个位置为开始/结尾的\(AA\)串的个数 那就直接做一下乘法就好 这个怎么求? 枚举一个位置 ...

  9. 【BZOJ4650】[NOI2016] 优秀的拆分(后缀数组)

    点此看题面 大致题意: 定义将一个字符串拆成\(AABB\)的形式为优秀拆分,求一个字符串所有子串的优秀拆分个数. 后缀数组 这题可是一道后缀数组黑题啊. 其实看完题解这题还是挺简单的. 大致思路 显 ...

随机推荐

  1. Mac 终端 显示隐藏文件

    defaults write com.apple.finder AppleShowAllFiles Yes && killall Finder //显示隐藏文件 defaults wr ...

  2. Java基础static的探究

    static方法就是没有this的方法. 在static方法内部不能调用非静态方法, 但是在非静态的方法中可以调用静态的方法和变量. 而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用stati ...

  3. SQL Server 数据库部分常用语句小结(一)

    1. 查询某存储过程的访问情况 SELECT TOP 1000 db_name(d.database_id) as DBName, s.name as 存储名字, s.type_desc as 存储类 ...

  4. c#高级编程_第10版 云盘地址

    下载地址 链接:https://pan.baidu.com/s/1u8PcY4RJhRB1yfm-2XaTEQ 密码:159z

  5. linux $参数

    $# 是传给脚本的参数个数 $0 是脚本本身的名字 $1 是传递给该shell脚本的第一个参数 $2 是传递给该shell脚本的第二个参数 $@ 是传给脚本的所有参数的列表 $* 是以一个单字符串显示 ...

  6. leaflet动态路径

    在leaflet中使用动态路径需要结合插件使用,对比了好几个插件,最终找到leaflet.motion比较合适: leaflet地址:https://leafletjs.com/ leaflet.mo ...

  7. python打印电脑串口的信息

    # -*- coding:utf-8 -*- from serial.tools.list_ports import comports port_list = list(comports()) if ...

  8. Django组件--forms组件(注册用)

    一.forms组件--校验类的使用 二.form组件--校验类的参数 三.forms组件校验的局部钩子--自定义校验规则(要看源码理解) 四.forms组件校验的全局钩子--校验form表单两次密码输 ...

  9. Redis学习笔记(3)——Redis的命令大全

    Redis是一种nosql数据库,常被称作数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted se ...

  10. Loj #3059. 「HNOI2019」序列

    Loj #3059. 「HNOI2019」序列 给定一个长度为 \(n\) 的序列 \(A_1, \ldots , A_n\),以及 \(m\) 个操作,每个操作将一个 \(A_i\) 修改为 \(k ...