[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[2][MN],rk[2][MN<<1],mn[MS][MN<<1],p;bool mul(int* rk,int* sa,int* RK,int* SA,int K){register int i;for (i=1;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+1;i<=n;++i) SA[mp[rk[i]]--]=i;for (i=1;i<=n;++i) RK[SA[i]]=RK[SA[i-1]]+(rk[SA[i]]!=rk[SA[i-1]]||rk[SA[i]+K]!=rk[SA[i-1]+K]);return RK[SA[n]]==n;}void presa(int* a){register int i,j,k;memset(mp,0,sizeof(mp));for (i=1;i<=n;++i) ++mp[a[i]];for (i=1;i<=26;++i) mp[i]+=mp[i-1];for (i=1;i<=n;++i) sa[0][mp[a[i]]--]=i;for (i=1;i<=n;++i) rk[0][sa[0][i]]=rk[0][sa[0][i-1]]+(a[sa[0][i]]!=a[sa[0][i-1]]);for (i=p=1;i<n;i<<=1,p^=1) if (mul(rk[p^1],sa[p^1],rk[p],sa[p],i)) break;if (i>=n) p^=1;for (i=1,j=0;i<=n;++i){for (k=sa[p][rk[p][i]-1];a[i+j]==a[k+j];++j);ht[rk[p][i]]=j; if (j) --j;}for (i=2;i<=n;++i) mn[0][i]=ht[i];for (i=1;i<MS;++i)for (j=2;j<=n;++j)mn[i][j]=min(mn[i-1][j],(j+mi[i-1]>n?INF:mn[i-1][j+mi[i-1]]));}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=0,f=1; char c=getchar();while (c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}while (c>='0' && c<='9') {n=n*10+c-'0'; c=getchar();}return n*f; }int main() {register int i,jl,jr,pre,x,y,T;T=read();for (mi[0]=1,lg[i=1]=0;i<MS;++i) mi[i]=mi[i-1]<<1,lg[mi[i]]=i; for (i=1;i<MN;++i) if (!lg[i]) lg[i]=lg[i-1];while (T--){memset(&s1,0,sizeof(s1));memset(&s2,0,sizeof(s2));memset(ltg,0,sizeof(ltg));memset(rtg,0,sizeof(rtg));scanf("%s",c+1); n=strlen(c+1); ans=0;a[n+1]=b[n+1]=rgs[n+1]=0;for (i=1;i<=n;++i) a[i]=c[i]-'a'+1; s1.presa(a);for (i=1;i<=n;++i) b[i]=a[n-i+1]; s2.presa(b);for (i=1;i<n;++i)for (jl=i,jr=jl+i,pre=0;jr<=n;jl=jr,jr+=i){if (jl<=pre) continue;y=s1.getmn(jl,jr); x=s2.getmn(n-jl+1,n-jr+1);if (x+y>i){++ltg[jl-x+1]; --ltg[jl+y-i+1];++rtg[jr+y-1]; --rtg[jr-x+i-1];}pre=jl+y-1;}for (i=1;i<=n;++i) lgs[i]=lgs[i-1]+ltg[i];for (i=n;i>=1;--i) rgs[i]=rgs[i+1]+rtg[i];for (i=2;i<=n;++i) ans+=1LL*lgs[i]*rgs[i-1];printf("%lld\n",ans);} }
Last Word
又到了愉快的吐槽时间O(∩_∩)O~(你吐槽给谁看呢)
这题基本就当做后缀数组的模板练习了,理解了模板敲起来就非常轻松了。
反正正解小C大概是想不到的,大致方向是没有错,可是有时候就是差那么一点点,在深入些就是正解。
但是离正解只差一步之遥却放弃的人不在少数啊。(但你至少可以拿到暴力分)
写的时候因为几个数组没清空WA了几个点,后缀数组构建的时候要用到大于n的下标,真讨厌。
转载于:https://www.cnblogs.com/ACMLCZH/p/6856865.html
[BZOJ]4650 优秀的拆分(Noi2016)相关推荐
- [BZOJ]4650 优秀的拆分(Noi2016)(哈希+二分)
传送门 题解 听说大佬们这题都是用SA秒掉的 然而SA的时间复杂度的确很优秀,缺点就是看不太懂-- 然后发现一位大佬用哈希华丽的过了此题,而且讲的特别清楚->这里 我们只要考虑以每一个点结尾的$ ...
- UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组、ST表)
UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组.ST表) 连NOI Day1T1都不会做...看了题解都写不出来还要抄Claris的代码.. 题 ...
- [NOI2016] 优秀的拆分 题解
[NOI2016] 优秀的拆分 题解 link 题意 \(T\) 组询问,每组一个字符串 \(s\) 求 \(s\) 所有字串分成 \(AABB\) 的方案数之和. \(A,B\) 为非空串. 题解 ...
- 【NOI2016】优秀的拆分(后缀数组)
题目描述 如果一个字符串可以被拆分为AABBAABB的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串aabaabaa,如果令 A=aab,B=a,我们就找 ...
- P1117 [NOI2016]优秀的拆分
$ \color{#0066ff}{ 题目描述 }$ 如果一个字符串可以被拆分为\(AABB\)的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串\(aab ...
- NOI2016 优秀的拆分(图解)
如果一个字符串可以被拆分为 AABB 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 aabaabaa,如果令 A=aab,B=a,我们就找到了这个字符 ...
- NOI 2016 优秀的拆分 (后缀数组+差分)
题目大意:给你一个字符串,求所有子串的所有优秀拆分总和,优秀的拆分被定义为一个字符串可以被拆分成4个子串,形如$AABB$,其中$AA$相同,$BB$相同,$AB$也可以相同 作为一道国赛题,95分竟 ...
- CSP - J 2020 T1 优秀的拆分
https://www.luogu.com.cn/problem/P7071 /* CSP - J 2020 T1 优秀的拆分 https://www.luogu.com.cn/problem/P70 ...
- 信息学奥赛一本通 2004:【20CSPJ普及组】优秀的拆分 | 洛谷 P7071 [CSP-J2020] 优秀的拆分
[题目链接] ybt 2004:[20CSPJ普及组]优秀的拆分 洛谷 P7071 [CSP-J2020] 优秀的拆分 [题目考点] 数制 基数:即进制数.十进制的基数是10,二进制的基数是2. 按位 ...
最新文章
- 搜集《ASP.NET中常用的26个优化性能方法》
- Keil5 STM32F系列 安装 安装包
- 子窗体与父窗体之间相互调用其方法的实现
- 邮件发送类,支持Gmail
- javaweb(二十一)——JavaWeb的两种开发模式
- KVM图形管理界面打不开(virt-manager 报错)
- js a/a中this的使用
- Mendeley简单操作
- AppScan介绍和安装
- 火狐安装网页视频下载插件(Video DownloadHelper)
- 【Python】 Python小游戏-贪吃蛇大冒险
- 个人免签约支付系统,收款就是这么简单
- stc 串口收发 c语言,STC12C5A60S2 串口中断接收程序
- 微信小程序聊天客服工具
- 词法分析、语法分析、语义分析
- Connection reset原因分析及解决思路
- RSS,从鲜果网和抓虾看起。
- 在Winform环境下Scottplot简单入门
- confluence 制作流程图_「每周开方」 高效制作流程图、思维导图
- 推荐五款免费且优质的自学网站,你值得拥有
热门文章
- 解压出来的文件md5会改变吗_监控Linux文件变化,防止系统被黑
- 2021扬州市高考成绩查询,2021高考成绩查询系统登录官网入口
- 傲梅备份服务器系统,傲梅轻松备份服务器版
- 还没有女朋友的朋友们,你们有福了,学会CycleGAN把男朋友变成女朋友
- mac安装rstudio_在Windows / Linux / Mac OS上安装R和RStudio入门
- log4j2自定义过滤器_Log4j级别示例–订单,优先级,自定义过滤器
- C++基础之函数的默认参数,什么是函数默认参数?
- 开课吧:继承是什么?继承的基础有什么?
- Docker之旅:了解Swarm集群
- 让Sublime Text 2支持GBK