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)\)。

//5892kb    784ms
#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
const int N=3e4+5;int Log[N];
struct Suffix_Array
{int tm[N],sa[N],sa2[N],rk[N],ht[N],st[N][15];inline void Init_ST(const int n){for(int i=1; i<=n; ++i) st[i][0]=ht[i];for(int j=1; j<=Log[n]; ++j)for(int t=1<<j-1,i=n-t; i; --i)st[i][j]=std::min(st[i][j-1],st[i+t][j-1]);}inline int LCP(int l,int r){l=rk[l], r=rk[r]; if(l>r) std::swap(l,r);++l;int k=Log[r-l+1];return std::min(st[l][k],st[r-(1<<k)+1][k]);}void Build(char *s,const int n){memset(rk,0,sizeof rk);memset(sa2,0,sizeof sa2);//要清空...! 因为下面比较懒得加<=n了。int m=26,*x=rk,*y=sa2;for(int i=0; i<=m; ++i) tm[i]=0;for(int i=1; i<=n; ++i) ++tm[x[i]=s[i]-'a'+1];for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];for(int i=n; i; --i) sa[tm[x[i]]--]=i;for(int k=1,p=0; k<n; k<<=1,m=p,p=0){for(int i=n-k+1; i<=n; ++i) y[++p]=i;for(int i=1; i<=n; ++i) if(sa[i]>k) y[++p]=sa[i]-k;for(int i=0; i<=m; ++i) tm[i]=0;for(int i=1; i<=n; ++i) ++tm[x[i]];for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];for(int i=n; i; --i) sa[tm[x[y[i]]]--]=y[i];std::swap(x,y), x[sa[1]]=p=1;for(int i=2; i<=n; ++i)x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p:++p;//because of thisif(p>=n) break;}for(int i=1; i<=n; ++i) rk[sa[i]]=i;ht[1]=0;for(int i=1,k=0,p; i<=n; ++i){if(rk[i]==1) continue;if(k) --k;p=sa[rk[i]-1];while(i+k<=n && p+k<=n && s[i+k]==s[p+k]) ++k;ht[rk[i]]=k;}Init_ST(n);}
}sa1,sa2;inline void Init_Log(const int n)
{for(int i=2; i<=n; ++i) Log[i]=Log[i>>1]+1;
}
void Solve()
{static int st[N],ed[N];static char s[N];scanf("%s",s+1); const int n=strlen(s+1);sa1.Build(s,n), std::reverse(s+1,s+1+n), sa2.Build(s,n);memset(st,0,n+1<<2), memset(ed,0,n+1<<2);for(int len=1,lim=n>>1; len<=lim; ++len)for(int i=len,j=len<<1; j<=n; i=j,j+=len){int x=std::min(len,sa2.LCP(n-i+1,n-j+1)),y=std::min(len,sa1.LCP(i,j));if(x+y-1>=len)++st[i-x+1], --st[i+y-len+1], ++ed[j-x+len], --ed[j+y];}LL ans=0;for(int i=1; i<n; ++i) st[i+1]+=st[i], ed[i+1]+=ed[i], ans+=1ll*ed[i]*st[i+1];printf("%lld\n",ans);
}int main()
{Init_Log(30000);int T; scanf("%d",&T);while(T--) Solve();return 0;
}

转载于:https://www.cnblogs.com/SovietPower/p/10199837.html

BZOJ.4650.[NOI2016]优秀的拆分(后缀数组 思路)相关推荐

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

    UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组.ST表) 连NOI Day1T1都不会做...看了题解都写不出来还要抄Claris的代码.. 题 ...

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

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

  3. [NOI2016] 优秀的拆分 题解

    [NOI2016] 优秀的拆分 题解 link 题意 \(T\) 组询问,每组一个字符串 \(s\) 求 \(s\) 所有字串分成 \(AABB\) 的方案数之和. \(A,B\) 为非空串. 题解 ...

  4. [BZOJ 3238] [AHOI 2013] 差异 【后缀数组 + 单调栈】

    题目链接:BZOJ - 3238 题目分析 显然,这道题就是求任意两个后缀之间的LCP的和,这与后缀数组的联系十分明显. 求出后缀数组后,求出字典序相邻两个后缀的LCP,即 Height 数组. 那么 ...

  5. bzoj 1031 [JSOI2007]字符加密Cipher 后缀数组

    题面 题目传送门 解法 后缀数组模板题吧-- 将字符串两倍,然后求一遍sa数组即可 时间复杂度:\(O(n\ log\ n)\) 代码 #include <bits/stdc++.h> # ...

  6. BZOJ 1031: [JSOI2007]字符加密Cipher( 后缀数组 )

    为什么我的后缀数组跑得这么慢... 把字符串复制一遍放在最后, 然后跑sa, 扫一遍就行了... --------------------------------------------------- ...

  7. P1117 [NOI2016]优秀的拆分

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

  8. [BZOJ]4199: [Noi2015]品酒大会(后缀数组+笛卡尔树)

    Time Limit: 10 Sec  Memory Limit: 512 MB Description Input Output Sample Input 10 ponoiiipoi 2 1 4 7 ...

  9. NOI2016 优秀的拆分(图解)

    如果一个字符串可以被拆分为 AABB 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 aabaabaa,如果令 A=aab,B=a,我们就找到了这个字符 ...

最新文章

  1. qa dataset
  2. Hollis原创|深入分析Java的编译原理
  3. Node.js「三」—— 创建静态 WEB 服务器
  4. Windows下redis使用及安装
  5. android 代码中使用dp,简单谈谈Android中SP与DP的区别
  6. 思维革命:交换两个变量的值
  7. oracle学习小结3之索引
  8. 五万以内买什么车合适?
  9. pyqt的listwidget 支持键盘搜索_键盘测评丨Ceke M87机械键盘:更好的双模MAC系统支持?...
  10. 极客唐小娟的故事-值得我们思考
  11. P4049 [JSOI2007]合金
  12. 代码管理学:安排新员工看代码,因为主管不会管理,不负责任
  13. 机器学习- 吴恩达Andrew Ng - week3-2 Logistic Regression Model
  14. Java内存映射原理与实现
  15. Postman设置环境变量
  16. 基于CC2430的基础实验6---UART串口实验
  17. 真正理解nodeJS,nodejs是什么,深入理解node
  18. H5编辑器H5-Dooring2.30更新指南
  19. 实用工具篇 | PPT图表制作软件
  20. 从键盘输入的年份,判断该年是否是闰年

热门文章

  1. marshaller java 乱码_Marshaller根据对象生成xml文件
  2. CGAL中Point的引用
  3. DE21 Convolution Formula
  4. Armadillo的安装
  5. EfficientNet-B4-Ranger:自然复杂环境下温室黄瓜病害识别新方法(同时存在两种疾病)
  6. 【matlab】线性空间滤波器的实现
  7. android 广播观察者,作业三——观察者模式在Android广播机制上的应用
  8. 我的世界java版地牢种子_我的世界手机版地牢种子代码汇总
  9. 用python开启相机_使用“打开”编辑相机设置
  10. 数据库管理学习笔记(一)——实体关系建模