BZOJ4650:[NOI2016]优秀的拆分——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=4650
https://www.luogu.org/problemnew/show/P1117
如果一个字符串可以被拆分为 AABB 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的。
例如,对于字符串 aabaabaa,如果令 A=aab,B=a,我们就找到了这个字符串拆分成 AABB的一种方式。
一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 A=a,B=baa,也可以用 AABB表示出上述字符串;但是,字符串 abaabaa 就没有优秀的拆分。
现在给出一个长度为 n的字符串 S,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。
以下事项需要注意:
1.出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。
2.在一个拆分中,允许出现 A=B。例如 cccc 存在拆分 A=B=c。
3.字符串本身也是它的一个子串。
这类题的方法还是见少了啊,听说神犇都是一眼秒的,果然还是有很大的差距啊,唉……
对于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(nlogn)。
注意,为了让复杂度正确,我们对区间的f和g差分。
- #include<cstdio>
- #include<cmath>
- #include<iostream>
- #include<vector>
- #include<cstring>
- #include<algorithm>
- #include<cctype>
- using namespace std;
- typedef long long ll;
- const int N=2e6+;
- char s[N];
- int n,m,rk[N],height[N],sa[N],w[N],cas,dp[N][],lg[N];
- int f[N],g[N];
- inline int qpow(int a){return <<a;}
- inline bool pan(int *x,int i,int j,int k){
- int ti=i+k<n?x[i+k]:-;
- int tj=j+k<n?x[j+k]:-;
- return ti==tj&&x[i]==x[j];
- }
- void SA_init(){
- int *x=rk,*y=height,r=;
- for(int i=;i<r;i++)w[i]=;
- for(int i=;i<n;i++)w[s[i]]++;
- for(int i=;i<r;i++)w[i]+=w[i-];
- for(int i=n-;i>=;i--)sa[--w[s[i]]]=i;
- r=;x[sa[]]=;
- for(int i=;i<n;i++)
- x[sa[i]]=s[sa[i]]==s[sa[i-]]?r-:r++;
- for(int k=;r<n;k<<=){
- int yn=;
- for(int i=n-k;i<n;i++)y[yn++]=i;
- for(int i=;i<n;i++)
- if(sa[i]>=k)y[yn++]=sa[i]-k;
- for(int i=;i<r;i++)w[i]=;
- for(int i=;i<n;i++)w[x[y[i]]]++;
- for(int i=;i<r;i++)w[i]+=w[i-];
- for(int i=n-;i>=;i--)sa[--w[x[y[i]]]]=y[i];
- swap(x,y);r=;x[sa[]]=;
- for(int i=;i<n;i++)
- x[sa[i]]=pan(y,sa[i],sa[i-],k)?r-:r++;
- }
- }
- inline void height_init(){
- int i,j,k=;
- for(int i=;i<=n;i++)rk[sa[i]]=i;
- for(int i=;i<n;i++){
- if(k)k--;
- j=sa[rk[i]-];
- while(s[i+k]==s[j+k])k++;
- height[rk[i]]=k;
- }
- }
- void st_init(){
- for(int i=;i<=n;i++){
- dp[i-][]=height[i];
- lg[i]=lg[i-];
- if((<<lg[i]+)==i)lg[i]++;
- }
- for(int j=;j<=lg[n];j++){
- for(int i=;i<n;i++){
- if(i+qpow(j)->=n)break;
- dp[i][j]=min(dp[i][j-],dp[i+qpow(j-)][j-]);
- }
- }
- }
- int lcp(int a,int b){
- int l=rk[a],r=rk[b];
- if(r<l)swap(l,r);
- l--;r--;
- if(r<)return ;
- l++;
- int len=r-l+;
- int k=lg[len];
- int h=qpow(k);
- return min(dp[l][k],dp[r-h+][k]);
- }
- int main(){
- scanf("%d",&cas);
- while(cas--){
- memset(f,,sizeof(f));
- memset(g,,sizeof(g));
- cin>>s;
- m=strlen(s),n=*m+;
- s[m]='$';
- for(int i=m+;i<n;i++){
- s[i]=s[n-i-];
- }
- s[n++]=;
- SA_init();
- n--;
- height_init();
- st_init();
- for(int l=;l<=m/;l++){
- for(int i=,j=l;j<m;i+=l,j+=l){
- int p=min(l,lcp(i,j));
- int s=min(l,lcp(n-i-,n-j-));
- if(p+s->=l){
- f[j-s+l]++;f[j+p]--;
- g[i-s+]++;g[i+p-l+]--;
- }
- }
- }
- ll ans=;
- for(int i=;i<m;i++){
- f[i]+=f[i-];
- g[i]+=g[i-];
- }
- for(int i=;i<m-;i++){
- ans+=(ll)f[i]*g[i+];
- }
- printf("%lld\n",ans);
- }
- return ;
- }
+++++++++++++++++++++++++++++++++++++++++++
+本文作者:luyouqi233。 +
+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+
+++++++++++++++++++++++++++++++++++++++++++
BZOJ4650:[NOI2016]优秀的拆分——题解的更多相关文章
- [UOJ#219][BZOJ4650][Noi2016]优秀的拆分
[UOJ#219][BZOJ4650][Noi2016]优秀的拆分 试题描述 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 A 和 B 是任意非空字符串,则我们称该字符串的这种拆分是优秀 ...
- BZOJ4650 [NOI2016]优秀的拆分 【后缀数组】
题目 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串,则我们称该字符串的这种拆 分是优秀的.例如,对于字符串 aabaabaa,如果令 A=aabA=aa ...
- 题解【bzoj4650 [NOI2016]优秀的拆分】
Description 求对每一个连续字串将它切割成形如 AABB 的形式的方案数之和 Solution 显然 AABB 是由两个 AA 串拼起来的 考虑维护两个数组 a[i] 和 b[i] ,其中 ...
- UOJ#219/BZOJ4650 [NOI2016]优秀的拆分 字符串 SA ST表
原文链接http://www.cnblogs.com/zhouzhendong/p/9025092.html 题目传送门 - UOJ#219 (推荐,题面清晰) 题目传送门 - BZOJ4650 题意 ...
- BZOJ4650 NOI2016优秀的拆分(后缀数组)
显然只要求出以每个位置开始的AA串数量就可以了,将其和反串同位置的结果乘一下,加起来就是答案.考虑对每种长度的字符串计数.若当前考虑的A串长度为x,我们每隔x个字符设一个关键点,求出相邻两关键点的后缀 ...
- [BZOJ4650][NOI2016]优秀的拆分(SAM构建SA)
关于解法这个讲的很清楚了,主要用了设关键点的巧妙思想. 主要想说的是一个刚学的方法:通过后缀自动机建立后缀树,再转成后缀数组. 后缀数组功能强大,但是最令人头疼的地方是模板太难背容易写错.用这个方法, ...
- BZOJ4650: [Noi2016]优秀的拆分
考场上没秒的话多拿5分并不划算的样子. 思想其实很简单嘛. 要统计答案,求以每个位置开始和结束的AA串数量就好了.那么枚举AA中A的长度L,每L个字符设一个关键点,这样AA一定经过相邻的两个关键点.计 ...
- bzoj千题计划317:bzoj4650: [Noi2016]优秀的拆分(后缀数组+差分)
https://www.lydsy.com/JudgeOnline/problem.php?id=4650 如果能够预处理出 suf[i] 以i结尾的形式为AA的子串个数 pre[i] 以i开头的形式 ...
- BZOJ4650: [Noi2016]优秀的拆分(hash 调和级数)
题意 题目链接 Sol NOI的题都这么良心么.. 先交个\(n^4\)暴力 => 75 hash优化一下 => 90 然后\(90\)到\(100\)分之间至少差了\(10\)难度台阶= ...
随机推荐
- xencenter迁移云主机方法
问题:POOL中计算节点内存不足. 解决方法:1.为计算节点添加内存(费用高)2.将部分资源迁移到其它POOL中. 方法: 1.选择要迁移的虚拟机 2.选择保存路径 这里可以看到可以批量导出: 注意: ...
- JDK1.8改为JDK1.7过程
电脑之前eclipse版本要求JDK1.8版本,现在要用jboss7.1做性能测试,目前仅支持JDK7.故需要降级. 网上有很多说把1.8删掉,这种做法我是不建议的,那么要用的时候呢?又得装回来多蛋疼 ...
- Jmeter做压力测试的心得
什么是性能压测? 也是最近刚刚接触到,就是被测试的系统,在一定的访问压力下,看程序运行是否稳定/服务器运行是否稳定,通常情况,是模拟多个请求同时 请求服务器,也就是在某个时间内,比如说1秒内,调用接口 ...
- [Clr via C#读书笔记]Cp5基元类型引用类型值类型
Cp5基元类型引用类型值类型 基元类型 编译器直接支持的类型,基元类型直接映射到FCL中存在的类型. 作者希望使用FCL类型名称而避免使用关键字.他的理由是为了更加的清晰的知道自己写的类型是哪种.但是 ...
- ARM架构中的程序执行与调用
ARM架构中的程序执行与调用 1. 几个名词 ABI : 可执行文件必须遵守的规范,以在特定执行环境中运行: 单独产生的可重定址的文件必须遵守的规范,以用来链接和执行. EABI: 适用于嵌入式环境的 ...
- 有关WCSF的几点整理
本文示例代码 一.CreateNew Attribute实现属性注入 Steps: 1/ aspx创建某个服务的属性. 2/ 为其添加[CreateNew] Attribute. 3/ 页面继承自Mi ...
- 关于docker 基础使用记录
Docker Hub地址:https://hub.docker.com Docker Hub 存放着 Docker 及其组件的所有资源.Docker Hub 可以帮助你与同事之间协作,并获得功能完整的 ...
- Discover the Web(栈模拟)
Description Standard web browsers contain features to move backward and forward among the pages rece ...
- HBase 参考文档翻译之 Getting Started
本篇是对HBase官方参考文档的大体翻译,介于本人英文水平实在有限,难免有纰漏之处.本篇不只是对官方文档的翻译,还加入了一些本人对HBase的理解.在翻译过程中,一些没有营养的废话,我就忽略了没有翻译 ...
- 算法与数据结构实验题 4.2 小 F 打怪
★实验任务 小 F 很爱打怪,今天因为系统 bug,他提前得知了 n 只怪的出现顺序以及击 倒每只怪得到的成就值 ai.设第一只怪出现的时间为第 1 秒,这个游戏每过 1 秒 钟出现一只新怪且没被击倒 ...