[BZOJ]4650 优秀的拆分(Noi2016)
比较有意思的一道后缀数组题。(小C最近是和后缀数组淦上了?)
放在NOI的考场上。O(n^3)暴力80分,O(n^2)暴力95分……
即使想把它作为一道签到题也不要这么随便啊摔(╯‵□′)╯︵┻━┻
Description
如果一个字符串可以被拆分为 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.字符串本身也是它的一个子串。
Input
每个输入文件包含多组数据。输入文件的第一行只有一个整数 T,表示数据的组数。保证 1≤T≤10。
接下来 T行,每行包含一个仅由英文小写字母构成的字符串 S,意义如题所述。
Output
输出 T行,每行包含一个整数,表示字符串 S 所有子串的所有拆分中,总共有多少个是优秀的拆分。
Sample Input
4
aabbbb
cccccc
aabaabaabaa
bbaabaababaaba
Sample Output
3
5
4
7
HINT
Solution
在考场上你只要负责打好95分的O(n^2)的字符串Hash就行了,代码总长只有30行。没有足够的时间和把握去打正解简直是作死。
如果你有认真思考过这一题,你大概会肯定这题的正解就是后缀数组吧。
设s[i]为字符串S的后缀,S[i]为字符串S的字符。
一个很容易想到的结论:对于两个原串上的后缀s[i]、s[j](i<j),如果最长公共前缀LCP(s[i],s[j])>=j-i,那么可以得到一个AA串。
如下图,i=1,j=3时,LCP(s[1],s[3])=3>3-1,得到AA串"abab"(S[1,4])。
事实上,从上例这个结论还可以找到另外一个AA串"baba"(S[2,5])。
于是我们发现,当i,j固定时,设len=j-i,Lcp=LCP(s[i],s[j])。
如果Lcp>=len,我们可以找到从i开始的Lcp-len+1个AA串,A的长度为len,如下图:
于是我们考虑找出所有这样的i、j,显然不可以直接枚举。
但我们又发现,如果LCP(s[i],s[j])<j-i,那么就一定不存在以S[i]~S[j-1]为开头的AA串。
那么感觉就可以跳着走?
考虑枚举A的长度x,即j-i。枚举i,每次把i加上x。
发现时间复杂度是调和级数,科学得不要不要的。
但问题来了,只求s[i]、s[j]的LCP肯定是有遗漏的,我们还需要求出前缀p[i]、p[j]的最长公共后缀LCS。(解释见下图)
那么判断存在AA串的条件就变成了Lcs+Lcp>len,可以找到Lcs+Lcp-len个AA串。
问题又来了,如果按这样统计答案的话,一些开头可能会被重复统计。
但是我们发现如果某一次的 i 还呆在上一次的 i 的Lcp和Lcs扩展出的区域时,它扩展出的区域将会和上一次是完全相同的。
那么我们果断选择跳过。(如下图)
求LCP的部分用ST表。
于是我们就O(nlogn)求出了以每个S[i]为开头的AA串的数量。计算答案的思路和O(n^2)的做法是一样的。
再求出以每个S[i]为结尾的AA串的数量,答案就是相邻两个字符开头数和结尾数相乘的总和……你懂的。
时间复杂度O(Tnlogn)。
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- #define ll long long
- #define INF 0x3FFFFFFF
- #define MS 17
- #define MN 30005
- using namespace std;
- int n,mp[MN],ht[MN],mi[MS],lg[MN];
- struct SufArr
- {
- int sa[][MN],rk[][MN<<],mn[MS][MN<<],p;
- bool mul(int* rk,int* sa,int* RK,int* SA,int K)
- {
- register int i;
- for (i=;i<=n;++i) mp[rk[sa[i]]]=i;
- for (i=n;i;--i) if (sa[i]>K) SA[mp[rk[sa[i]-K]]--]=sa[i]-K;
- for (i=n-K+;i<=n;++i) SA[mp[rk[i]]--]=i;
- for (i=;i<=n;++i) RK[SA[i]]=RK[SA[i-]]+(rk[SA[i]]!=rk[SA[i-]]||rk[SA[i]+K]!=rk[SA[i-]+K]);
- return RK[SA[n]]==n;
- }
- void presa(int* a)
- {
- register int i,j,k;
- memset(mp,,sizeof(mp));
- for (i=;i<=n;++i) ++mp[a[i]];
- for (i=;i<=;++i) mp[i]+=mp[i-];
- for (i=;i<=n;++i) sa[][mp[a[i]]--]=i;
- for (i=;i<=n;++i) rk[][sa[][i]]=rk[][sa[][i-]]+(a[sa[][i]]!=a[sa[][i-]]);
- for (i=p=;i<n;i<<=,p^=) if (mul(rk[p^],sa[p^],rk[p],sa[p],i)) break;
- if (i>=n) p^=;
- for (i=,j=;i<=n;++i)
- {
- for (k=sa[p][rk[p][i]-];a[i+j]==a[k+j];++j);
- ht[rk[p][i]]=j; if (j) --j;
- }
- for (i=;i<=n;++i) mn[][i]=ht[i];
- for (i=;i<MS;++i)
- for (j=;j<=n;++j)
- mn[i][j]=min(mn[i-][j],(j+mi[i-]>n?INF:mn[i-][j+mi[i-]]));
- }
- int getmn(int x,int y)
- {
- x=rk[p][x]; y=rk[p][y];
- if (x>y) swap(x,y);
- y=y-x; ++x;
- return min(mn[lg[y]][x],mn[lg[y]][x+y-mi[lg[y]]]);
- }
- }s1,s2;
- ll ans;
- int a[MN],b[MN],ltg[MN],rtg[MN],lgs[MN],rgs[MN];
- char c[MN];
- inline int read()
- {
- int n=,f=; char c=getchar();
- while (c<'' || c>'') {if(c=='-')f=-; c=getchar();}
- while (c>='' && c<='') {n=n*+c-''; c=getchar();}
- return n*f;
- }
- int main()
- {
- register int i,jl,jr,pre,x,y,T;
- T=read();
- for (mi[]=,lg[i=]=;i<MS;++i) mi[i]=mi[i-]<<,lg[mi[i]]=i;
- for (i=;i<MN;++i) if (!lg[i]) lg[i]=lg[i-];
- while (T--)
- {
- memset(&s1,,sizeof(s1));
- memset(&s2,,sizeof(s2));
- memset(ltg,,sizeof(ltg));
- memset(rtg,,sizeof(rtg));
- scanf("%s",c+); n=strlen(c+); ans=;
- a[n+]=b[n+]=rgs[n+]=;
- for (i=;i<=n;++i) a[i]=c[i]-'a'+; s1.presa(a);
- for (i=;i<=n;++i) b[i]=a[n-i+]; s2.presa(b);
- for (i=;i<n;++i)
- for (jl=i,jr=jl+i,pre=;jr<=n;jl=jr,jr+=i)
- {
- if (jl<=pre) continue;
- y=s1.getmn(jl,jr); x=s2.getmn(n-jl+,n-jr+);
- if (x+y>i)
- {
- ++ltg[jl-x+]; --ltg[jl+y-i+];
- ++rtg[jr+y-]; --rtg[jr-x+i-];
- }
- pre=jl+y-;
- }
- for (i=;i<=n;++i) lgs[i]=lgs[i-]+ltg[i];
- for (i=n;i>=;--i) rgs[i]=rgs[i+]+rtg[i];
- for (i=;i<=n;++i) ans+=1LL*lgs[i]*rgs[i-];
- printf("%lld\n",ans);
- }
- }
Last Word
又到了愉快的吐槽时间O(∩_∩)O~(你吐槽给谁看呢)
这题基本就当做后缀数组的模板练习了,理解了模板敲起来就非常轻松了。
反正正解小C大概是想不到的,大致方向是没有错,可是有时候就是差那么一点点,在深入些就是正解。
但是离正解只差一步之遥却放弃的人不在少数啊。(但你至少可以拿到暴力分)
写的时候因为几个数组没清空WA了几个点,后缀数组构建的时候要用到大于n的下标,真讨厌。
[BZOJ]4650 优秀的拆分(Noi2016)的更多相关文章
- [BZOJ]4650 优秀的拆分(Noi2016)(哈希+二分)
传送门 题解 听说大佬们这题都是用SA秒掉的 然而SA的时间复杂度的确很优秀,缺点就是看不太懂…… 然后发现一位大佬用哈希华丽的过了此题,而且讲的特别清楚->这里 我们只要考虑以每一个点结尾 ...
- bzoj 4650(洛谷 1117) [Noi2016]优秀的拆分——枚举长度的关键点+后缀数组
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4650 https://www.luogu.org/problemnew/show/P1117 ...
- loj2083 优秀的拆分 [NOI2016] SA
正解:SA 解题报告: 我永远喜欢loj! 显然$AABB$串相当于是由两个$AA$串拼起来的,所以可以先考虑如果求出来了所有$AA$串怎么求答案? 就假如能统计出$st[i]$表示所有以$i$为开头 ...
- [NOI2016]优秀的拆分 后缀数组
题面:洛谷 题解: 因为对于原串的每个长度不一定等于len的拆分而言,如果合法,它将只会被对应的子串统计贡献. 所以子串这个限制相当于是没有的. 所以我们只需要对于每个位置i求出f[i]表示以i为开头 ...
- [BZOJ]4650: [Noi2016]优秀的拆分
Time Limit: 30 Sec Memory Limit: 512 MB Description 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串, ...
- 【刷题】BZOJ 4650 [Noi2016]优秀的拆分
Description 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串,则我们称该字符串的这种拆分是优秀的.例如,对于字符串 aabaabaa,如果令 A ...
- [LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分
[LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分 题意 给定一个字符串 \(S\), 求有多少种将 \(S\) 的子串拆分为形如 AABB 的拆分方案 \(| ...
- [bzoj 4650][NOI 2016]优秀的拆分
传送门 Description 如果一个字符串可以被拆分为\(AABB\) 的形式,其中$ A$和 \(B\)是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串\(aabaaba ...
- 【BZOJ4650】【NOI2016】优秀的拆分(后缀数组)
[BZOJ4650][NOI2016]优秀的拆分(后缀数组) 题面 BZOJ Uoj 题解 如果我们知道以某个位置为开始/结尾的\(AA\)串的个数 那就直接做一下乘法就好 这个怎么求? 枚举一个位置 ...
随机推荐
- SELinux与进程管理
- STL之queue
描述 使用STL中的queue,完成入队.出队.获取队首.获取队尾等基本操作. 部分代码已经给出,请补充完整,提交时请勿包含已经给出的代码. int main() { queue<int> ...
- C# 使用 ffmpeg 进行音频转码
先放一下 ffmpeg 的官方文档以及下载地址: 官方文档:http://ffmpeg.org/ffmpeg.html 下载地址:http://ffmpeg.org/download.html 用 f ...
- 数据库 MYSQL操作(一)
数据库 MYSQL操作总结(一) 本文主要介绍一下笔者在使用数据库操作的过程中的一些总结,主要的内容包括一下几个内容: 一.mysql 使用基础(主要包括数据库的安装.基本操作等内容) 二.mysq ...
- C语言学习(一)
C语言易学难精,如果在平时的编程中,加入一些小技巧,可以提供程序运行的效率,何乐而不为呢? 本小白初学C语言准备记录自己的学C之路,经常贴一些自己觉得优化的小程序代码,希望大神们不吝 赐教. 宏定义下 ...
- spring-oauth-server实践:使用授权方式四:client_credentials 模式下access_token做业务!!!
spring-oauth-server入门(1-10)使用授权方式四:client_credentials 模式下access_token做业务!!! 准备工作 授权方式四::客户端方式: 服务网关地 ...
- 发布到NPMJS
最近在做微服务的前后端设计,打算将客户端中的一个模块独立出来发布到npmjs上,因此,有机会了解了一下npm的发布过程. 参考了很多网上的文章,长篇累牍(但在这里还是真心感谢他们的分享),最终总结成一 ...
- 您的 Java 代码安全吗 — 还是暴露在外? 【转】
在开发 Java Web 应用程序时,您需要确保应用程序拥有完善的安全性特征补充.这里在谈到 Java 安全性时,我们并不谈及 Java 语言提供的安全性 API,也不涉及使用 Java 代码来保护应 ...
- 三十天学不会TCP,UDP/IP网络编程 -- RTT的计算
欢迎去gitbook(https://www.gitbook.com/@rogerzhu/)看到完整版. 如果对和程序员有关的计算机网络知识,和对计算机网络方面的编程有兴趣,虽然说现在这种“看不见”的 ...
- Struts(十六):通过CURD来学习Struts流程及ModelDriven的用法
背景: 从一个Member的增删改查,来了解Struts2的运行原理及学习ModelDriven拦截器.Preparable拦截器. 新建项目实现列表的展示及删除功能: web.xml <?xm ...