NOI 2016 优秀的拆分 (后缀数组+差分)
题目大意:给你一个字符串,求所有子串的所有优秀拆分总和,优秀的拆分被定义为一个字符串可以被拆分成4个子串,形如$AABB$,其中$AA$相同,$BB$相同,$AB$也可以相同
作为一道国赛题,95分竟然就这么给我们了!只是一个$NOIP$难度的哈希套$DP$啊......
95分就是从后往前找,统计$AA$串,每次统计一下从这个位置开始的所有子串 和 紧随其后的等长串 相同的个数$sum$
$hash(i,i+j-1)==hash(i+j,i+2*j-1) sum[i]++$
然后再统计$BB$串,就是加上在这两个串之后的位置的$sum$值,这样就统计出了合法拆分数
$dp[i]+=sum[i+2*j]$
95分就这样到手啦,竟然连自然溢出hash都不卡
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define ull unsigned long long
#define N 2050
#define seed 233
#define idx(x) (x-'a'+1)
using namespace std;
//re
int T,len;
char str[N];
int dp[N],sum[N];
ull hsh[N],sp[N];
ull ghsh(int x,int y) {return hsh[y]-hsh[x-]*sp[y-x+];}
void clr()
{
memset(dp,,sizeof(dp));
memset(sp,,sizeof(sp));
memset(sum,,sizeof(sum));
memset(hsh,,sizeof(hsh));
}
int main()
{
scanf("%d",&T);
while(T--)
{
clr();
scanf("%s",str+);
len=strlen(str+);
sp[]=;
for(int i=;i<=len;i++)
hsh[i]=hsh[i-]*seed+idx(str[i]),
sp[i]=sp[i-]*seed;
for(int i=len-;i>=;i--)
{
for(int j=;i+*j-<=len;j++)
{
if(ghsh(i,i+j-)==ghsh(i+j,i+*j-)){
dp[i]++;
sum[i]+=dp[i+*j];
}
}
}
int ans=;
for(int i=;i<=len;i++)
ans+=sum[i];
printf("%d\n",ans);
}
return ;
}
接下来才是本题的重点!$NOI$岂是你想$AK$就$AK$的!出题人可能是想针对某位巨佬
不看题解这个正解思路真是很难想到......
思路大概是这样的,其实我们每次只需要统计$AA$串的数量就行了,因为$AABB$串的数量等于,在$i-1$位结束的$AA$串的数量乘以在第$i$位开始的$AA$串的数量,用公式表示就是
接下来就是统计$st$和$ed$了,感觉正解的思路很神
先用后缀数组+$ST$表处理出任意两个位置,作为后缀串开头的$LCP$以及作为前缀串末尾的$LCS$,求$LCS$可以把串反着读再去套$SA$
每次选取一个$A$串的长度$len$,再在原串每隔$len$的位置设一个关键点
然后会发现一个神奇的性质,任意一个长度为$2*len$的串必然经过且仅经过2个关键点!
接下来统计经过所有长度为$2*len$所有$AA$串,比如选定两个关键点$a,b$,且$a+len=b$
如果把$a$的和b$相同的前缀和后缀拼在一起,且拼完之后长度>=len,说明存在至少一个$AA$串
可以把$AA$串想象成一个块,在两个关键点上移动,块的左端点不能大于$a$,块的右端点不能小于$b$,块不能移动到上一个或者下一个关键点的位置,块内必须是$AA$串,这样,块所有能移动的位置-块的长度$len$,就是经过$ab$两个关键点的所有$AA$串数
时间变成,$logn$是调和级数的近似值
细节比较多需要仔细思考
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define N 30100
#define seed 233
#define idx(x) (x-'a'+1)
using namespace std;
//re
int T,len;
int lg[N];
ll st[N],ed[N],S[N],E[N];
struct suffix{
char str[N];
int sa[N],tr[N],rk[N],hs[N],h[N],f[N][],len;
bool check(int k,int x,int y){
if(x+k>len||y+k>len) return ;
else return (rk[x]==rk[y]&&rk[x+k]==rk[y+k])?:;
}
void get_suffix()
{
int cnt=,i;len=strlen(str+);
for(i=;i<=len;i++) hs[str[i]]++;
for(i=;i<=;i++) if(hs[i]) tr[i]=++cnt;
for(i=;i<=;i++) hs[i]+=hs[i-];
for(i=;i<=len;i++) rk[i]=tr[str[i]],sa[hs[str[i]]--]=i;
for(int k=;cnt<len;k<<=)
{
for(i=;i<=cnt;i++) hs[i]=;
for(i=;i<=len;i++) hs[rk[i]]++;
for(i=;i<=cnt;i++) hs[i]+=hs[i-];
for(i=len;i>=;i--) if(sa[i]>k) tr[sa[i]-k]=hs[rk[sa[i]-k]]--;
for(i=;i<=k;i++) tr[len-i+]=hs[rk[len-i+]]--;
for(i=;i<=len;i++) sa[tr[i]]=i;
for(i=,cnt=;i<=len;i++) tr[sa[i]]=check(k,sa[i],sa[i-])?cnt:++cnt;
for(i=;i<=len;i++) rk[i]=tr[i];
}
for(i=;i<=len;i++)
{
if(rk[i]==) continue;
for(int j=max(,h[rk[i-]]-);;j++)
if(str[i+j-]==str[sa[rk[i]-]+j-]) h[rk[i]]=j;
else break;
}
}
void get_ST()
{
for(int i=;i<=len;i++)
f[i][]=h[i];
for(int k=;(<<k)<=len;k++)
for(int i=;i+(<<k)-<=len;i++)
f[i][k]=min(f[i][k-],f[i+(<<(k-))][k-]);
}
int query(int x,int y)
{int L=y-x+;
return min(f[x][lg[L]],f[y-(<<lg[L])+][lg[L]]);}
}p,s;
void clr()
{
memset(S,,sizeof(S)),memset(st,,sizeof(st));
memset(E,,sizeof(E)),memset(ed,,sizeof(ed));
memset(&p,,sizeof(p)),memset(&s,,sizeof(s));
}
int main()
{
scanf("%d",&T);
for(int i=;i<=;i++)
lg[i]=lg[i>>]+;
while(T--)
{
clr();
scanf("%s",s.str+);
int len=strlen(s.str+);
for(int i=;i<=len;i++)
p.str[i]=s.str[len-i+];
s.get_suffix();
s.get_ST();
p.get_suffix();
p.get_ST();
int sx,sy,px,py,ss,sp,l,r;
for(int i=;i<=len;i++)
{
for(int j=i+;j<=len;j+=i)
{
sx=s.rk[j-i+],sy=s.rk[j+];
if(sx>sy) swap(sx,sy);
px=p.rk[len-(j-i)+],py=p.rk[len-j+];
if(px>py) swap(px,py);
ss=s.query(sx+,sy);
sp=p.query(px+,py);
if(ss+sp<i||sp==) continue;
l=max(j-i-i+,j-i-sp+);
r=min(j+i-,j+ss);
S[l]++,S[r-*i+]--;
E[l+*i-]++,E[r+]--;
}
}
for(int i=;i<=len;i++)
st[i]=st[i-]+S[i],ed[i]=ed[i-]+E[i];
ll ans=;
for(int i=;i<=len;i++)
ans+=ed[i-]*st[i];
printf("%lld\n",ans);
}
return ;
}
NOI 2016 优秀的拆分 (后缀数组+差分)的更多相关文章
- [LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分
[LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分 题意 给定一个字符串 \(S\), 求有多少种将 \(S\) 的子串拆分为形如 AABB 的拆分方案 \(| ...
- 字符串(后缀自动机):NOI 2016 优秀的拆分
[问题描述] 如果一个字符串可以被拆分为 AABB 的形式,其中 A 和 B 是任意非空字符串, 则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 aabaabaa,如果令 A = aab, B ...
- [NOI 2016]优秀的拆分
Description 题库链接 给你一个长度为 \(n\) 的只含小写字母的字符串 \(S\) ,计算其子串有多少优秀的拆分. 如果一个字符串能被表示成 \(AABB\) 的形式,其中 \(A,B\ ...
- UOJ#219. 【NOI2016】优秀的拆分 [后缀数组 ST表]
#219. [NOI2016]优秀的拆分 题意:求有多少AABB样子的子串,拆分不同的同一个子串算多个 一开始一直想直接求,并不方便 然后看了一眼Claris的题解的第一行就有思路了 如果分开,求\( ...
- [NOI2016]优秀的拆分 后缀数组
题面:洛谷 题解: 因为对于原串的每个长度不一定等于len的拆分而言,如果合法,它将只会被对应的子串统计贡献. 所以子串这个限制相当于是没有的. 所以我们只需要对于每个位置i求出f[i]表示以i为开头 ...
- [bzoj 4650][NOI 2016]优秀的拆分
传送门 Description 如果一个字符串可以被拆分为\(AABB\) 的形式,其中$ A$和 \(B\)是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串\(aabaaba ...
- UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组、ST表)
连NOI Day1T1都不会做...看了题解都写不出来还要抄Claris的代码.. 题目链接: (luogu)https://www.luogu.org/problemnew/show/P1117 ( ...
- BZOJ.4650.[NOI2016]优秀的拆分(后缀数组 思路)
BZOJ 洛谷 令\(st[i]\)表示以\(i\)为开头有多少个\(AA\)这样的子串,\(ed[i]\)表示以\(i\)结尾有多少个\(AA\)这样的子串.那么\(Ans=\sum_{i=1}^{ ...
- luogu1117 优秀的拆分 (后缀数组)
考虑分别计算每个位置作为AA的末尾或者BB的开头的个数 最后乘一乘就是答案 据说是套路的计算AA的方法: 首先枚举A的长度L,然后每L个字符当做一个关键点,这样的话,一个AA包含且只包含相邻两个关键点 ...
随机推荐
- [网络流24题] 太空飞行计划问题 (最大流->最大权闭合图)
洛谷传送门 LOJ传送门 做这道题之前建议先看这篇论文,虽然论文里很多地方用了很多术语,但hbt神犇讲得很明白 这篇题解更加偏向于感性理解 把问题放到二分图上,左侧一列点是实验,权值为$p[i]$,右 ...
- 【XSY2384】【GDOI2017】微信
致去年的我:这是道广义SAM模板题啊…… 题意: Description Input Output HINT $1\leq N\leq 20$,$1\leq Q\leq 10^5$,字符串总长$\le ...
- 关于idea控制台乱码问题
乱码是常有的事儿,改一下也就两分钟......不多说看图: 上图中的勾选项一定不要忘记,它可以隐藏你项目中encoding设置. 在上图两个文件中加入 -Dfile.encoding=UTF-8 在上 ...
- openwrt针对RT5350代码下载,配置和编译
转载地址:http://blog.csdn.net/dean_gdp/article/details/37091685 近期买了块官方板的RT5350: 先介绍代码下载.下面命令都是用登录用户运行,无 ...
- JeeSite(2):导入数据,进入系统
本文的原文连接是: http://blog.csdn.net/freewebsys/article/details/50954485 未经博主同意不得转载. 博主地址是:http://blog.csd ...
- Introduction to CMMI培训总结
6月3日到5日,用了3天时间,參加了cmmi-dev的简单介绍课程培训,參加培训真的比上班都要累非常多啊!每天早上9点到下午6点.中午吃饭加歇息总共1小时.晚上还有作业要做,每天睡觉都要到11点 ...
- Linux 网卡驱动学习(六)(应用层、tcp 层、ip 层、设备层和驱动层作用解析)
本文将介绍网络连接建立的过程.收发包流程,以及当中应用层.tcp层.ip层.设备层和驱动层各层发挥的作用. 1.应用层 对于使用socket进行网络连接的server端程序.我们会先调用socket函 ...
- No unique bean of type [net.shougongfang.action.paymoney.AlipayPayMoneyReturnObj] is defined: Unsat
0 你把@Service放到实现类上吧.这个问题好像不止一个人在问啦 2013年10月25日 10:34 shidan66 30 0 1 1 加入评论 00 1,@service放到实现上 2. ...
- c3---scanf
#include <stdio.h> int main(int argc, const char * argv[]) { // 要求: 存储用户输入的整数 // 1.用户输入的整数确定吗? ...
- CXF WebService中传递复杂对象(List、Map、Array)
转自:https://wenku.baidu.com/view/047ce58ed0d233d4b14e69eb.html 现在开始介绍传递复杂类型的对象.如JavaBean.Array.List.M ...