[Noi2016]优秀的拆分
来自F allDream的博客,未经允许,请勿转载,谢谢。
如果一个字符串可以被拆分为 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.字符串本身也是它的一个子串
你需要计算一个字符串的所有子串的优秀的拆分的个数之和
T(T<=10)组数据,n<=30000
这道题n^2暴力95分..
正解的话反正我是想不到...
如果知道以每个点为左/右端点构成AA的方案数,就可以计算答案
考虑求出正着反着各求出后缀数组之后,枚举长度L,然后每段长度是L把这个字符串分段
每次枚举两个端点i,j(i和j都是L的倍数,j=i+L),并且计算左端点在[i-L+1,i]内的答案。
所以求一下点i,j正着的lcp,反着的lcp,也就是从点i开始能向左右扩展到哪里,如果能扩展到的长度大等于L,那么就有答案
假设倒着时候的lcp是a,正着的是b,也就是[i-a+1,i]和[j-a+1,j]相同(显然a>0),b同理,那么有答案的左端点区间是[i-a+1,i-a+1 + (a+b-1-L) -1] 差分即可。
复杂度Tnlogn
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define MN 30000
#define MD 15
using namespace std;
inline int read()
{
int x = ; char ch = getchar();
while(ch < '' || ch > '') ch = getchar();
while(ch >= '' && ch <= ''){x = x * + ch - '';ch = getchar();}
return x;
} int n,k,lt[MN+],rt[MN+],v[MN+],Log[MN+];ll ans;
char st[MN*+],st2[MN*+]; void CalSa(int*SA,int*RK,int*sa,int*rk)
{
for(int i=;i<=n;++i) v[rk[sa[i]]]=i;
for(int i=n;i;--i)
if(sa[i]>k) SA[v[rk[sa[i]-k]]--]=sa[i]-k;
for(int i=n-k+;i<=n;++i) SA[v[rk[i]]--]=i;
for(int i=;i<=n;++i)
RK[SA[i]]=RK[SA[i-]]+(rk[SA[i]]!=rk[SA[i-]]||rk[SA[i]+k]!=rk[SA[i-]+k]);
} void GetH(int*H,int*sa,int*rk,char*s)
{
for(int i=,k=;i<=n;H[rk[i++]-]=k,k?--k:) if(rk[i]>)
for(int j=sa[rk[i]-];s[j+k]==s[i+k];++k); } struct SuffixArray
{
int p,q,sa[][MN+],rk[][MN+],f[MD+][MN+],F[MD+][MN+],fa[MD+][MN+],Fa[MD+][MN+];
inline void Clear(){p=;q=;
memset(rk,,sizeof(rk));
memset(sa,,sizeof(sa));
}
void Build(char *s)
{
memset(v,,sizeof(int)*);
for(int i=;i<=n;++i)++v[s[i]-'a'];
for(int i=;i<;++i) v[i]+=v[i-];
for(int i=;i<=n;++i) sa[q][v[s[i]-'a']--]=i;
for(int i=;i<=n;++i)
rk[q][sa[q][i]]=rk[q][sa[q][i-]]+(s[sa[q][i]]!=s[sa[q][i-]]);
for(k=;k<n;k<<=)
{
CalSa(sa[p],rk[p],sa[q],rk[q]);
swap(p,q);
}
}
void BuildHeight(char *s)
{
GetH(f[],sa[q],rk[q],s);
for(int i=;i<=n;++i) F[][i]=f[][i-];
for(int i=;i<=MD;++i) F[i][]=f[i][]=f[i][n]=F[i][]=MN;
for(int i=;i<n;++i) fa[][i]=i+,Fa[][i+]=i;
for(int i=;i<=MD;++i)
for(int j=;j<=n;++j)
fa[i][j]=fa[i-][fa[i-][j]],
Fa[i][j]=Fa[i-][Fa[i-][j]],
f[i][j]=min(f[i-][j],f[i-][fa[i-][j]]),
F[i][j]=min(F[i-][j],F[i-][Fa[i-][j]]);
}
int query(int i,int j)
{
i=rk[q][i],j=rk[q][j];
if(i>j) swap(i,j);
int L=Log[j-i];
return min(f[L][i],F[L][j]);
}
}P,S; int main()
{
Log[]=-;
for(int i=;i<=MN;++i) Log[i]=Log[i>>]+;
for(int T=read();T;--T)
{
scanf("%s",st+);n=strlen(st+);
for(int i=;i<=n;++i) st2[i]=st[n-i+];
P.Clear();S.Clear();ans=;
P.Build(st);S.Build(st2);
P.BuildHeight(st);
S.BuildHeight(st2);
memset(lt,,sizeof(lt));
memset(rt,,sizeof(rt));
for(int L=;L<=n;++L)
for(int i=L,j=i+L;j<=n;i=j,j+=L)
{
int a=P.query(i,j),b=S.query(n-j+,n-i+);
if(min(a,L)+min(b,L)->=L)
{
int len=min(a,L)+min(b,L)--L;
++lt[i-min(b,L)+],--lt[i-min(b,L)+len+],
++rt[j-min(b,L)+L],--rt[j-min(b,L)+L+len+];
}
}
for(int i=;i<=n;++i) lt[i]+=lt[i-],rt[i]+=rt[i-];
for(int i=;i<n;++i) ans+=1LL*rt[i]*lt[i+];
printf("%lld\n",ans);
}
return ;
}
[Noi2016]优秀的拆分的更多相关文章
- [NOI2016]优秀的拆分&&BZOJ2119股市的预测
[NOI2016]优秀的拆分 https://www.lydsy.com/JudgeOnline/problem.php?id=4650 题解 如果我们能够统计出一个数组a,一个数组b,a[i]表示以 ...
- luogu1117 [NOI2016]优秀的拆分
luogu1117 [NOI2016]优秀的拆分 https://www.luogu.org/problemnew/show/P1117 后缀数组我忘了. 此题哈希可解决95分(= =) 设\(l_i ...
- 【BZOJ4560】[NOI2016]优秀的拆分
[BZOJ4560][NOI2016]优秀的拆分 题面 bzoj 洛谷 题解 考虑一个形如\(AABB\)的串是由两个形如\(AA\)的串拼起来的 那么我们设 \(f[i]\):以位置\(i\)为结尾 ...
- [UOJ#219][BZOJ4650][Noi2016]优秀的拆分
[UOJ#219][BZOJ4650][Noi2016]优秀的拆分 试题描述 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 A 和 B 是任意非空字符串,则我们称该字符串的这种拆分是优秀 ...
- [NOI2016]优秀的拆分(SA数组)
[NOI2016]优秀的拆分 题目描述 如果一个字符串可以被拆分为 \(AABB\) 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 \(aabaaba ...
- 题解-NOI2016 优秀的拆分
NOI2016 优秀的拆分 \(T\) 组测试数据.求字符串 \(s\) 的所有子串拆成 \(AABB\) 形式的方案总和. 数据范围:\(1\le T\le 10\),\(1\le n\le 3\c ...
- [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 ...
随机推荐
- bzoj千题计划113:bzoj1023: [SHOI2008]cactus仙人掌图
http://www.lydsy.com/JudgeOnline/problem.php?id=1023 dp[x] 表示以x为端点的最长链 子节点与x不在同一个环上,那就是两条最长半链长度 子节点与 ...
- EasyUI中, datagrid用loadData方法绑定数据。
$("#dg").datagrid("loadData", { , " }, { "ck": "1", &qu ...
- php代码一样,编码不同报错
php代码一样,编码不同报错 两个php代码完全一样,但是就报错,比如说声明比如在very first,这种,可以把编码设置utf-8 无bom
- LeetCode & Q53-Maximum Subarray-Easy & 动态规划思路分析
Array DP Divide and Conquer Description: Find the contiguous subarray within an array (containing at ...
- javascript递归函数
递归函数:是指函数直接或间接调用函数本身,则称该函数为递归函数. 这句话理解起来并不难,从概念上出发,给出以下的例子: function foo(){ console.log("函数 foo ...
- Andrew Ng机器学习第一章——单变量线性回归
监督学习算法工作流程 h代表假设函数,h是一个引导x得到y的函数 如何表示h函数是监督学习的关键问题 线性回归:h函数是一个线性函数 代价函数 在线性回归问题中,常常需要解决最小化问题.代价函数常用平 ...
- maven 每次update后影响接口实现类的问题
遇到maven每次update后,就会更改eclipse中java Compiler中的jdk compliance版本 <plugin> <groupId>org.apach ...
- SpringBoot应用的集成测试
一.概念和定义 进行软件开发的时候,我们会写很多代码,不过,再过六个月(甚至一年以上)你知道自己的代码怎么运作么?通过测试(单元测试.集成测试.接口测试)可以保证系统的可维护性,当我们修改了某些代码时 ...
- api-gateway实践(04)新服务网关 - 新手入门
一.网关引擎环境 1.下载代码 2.搭建环境 3.打包部署 二.配置中心环境 1.下载代码 2.搭建环境 3.打包部署 三.创建业务实例 1.以租户身份登录配置中心,注册 group.version. ...
- oracle:批量插入不同方案对比
实时测试的速度: --48466条数据 --1.297 inline view更新法 inline view更新法就是更新一个临时建立的视图 update (select a.join_stateas ...