[LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分
[LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分
题意
给定一个字符串 \(S\), 求有多少种将 \(S\) 的子串拆分为形如 AABB 的拆分方案
\(|S|\le 30000\) (\(95\%\) 数据 \(|S|\le 2000\))
题解
考场上遇见这题直接打95分暴力哈希跑路就完事了吧
\(O(n^2)\) 暴力就直接枚举所有子串看它是不是 AA 型的, 在左右端点处分别标记一下, 然后枚举断点把两边的方案数乘起来就完事了.
考虑优化这个暴力. 我们枚举这个 AA 串中 A 的长度 \(l\), 然后每隔 \(l\) 取一个关键点, 那么每个 AA 串必然会覆盖两个关键点. 对于每对相邻的关键点 \(a\) 和 \(b\), 我们计算 \(p=\operatorname{LCP}(S[a:],S[b:])\) 以及 \(s=\operatorname{LCS}(S[:a-1],S[:b-1])\) . 那么只要 \(p+s\ge l\) 就会有 AA 串出现. 画画图可以发现 \([a-s,a-(l-p)]\) 都可以是 AA 串的左端点. 直接在差分数组上修改左右端点就可以了. 注意只能计算同时包含这两个相邻的关键点的 AA 串, 所以应该和 \([a-l+1,a]\) 取交集. 算完之后前缀和一下按照 \(O(n^2)\) 暴力里的操作算最终答案就可以了.
参考代码
#include <bits/stdc++.h>
const int MAXN=1e5+10;
typedef long long intEx;
struct SuffixArray{
char s[MAXN];
int SA[MAXN];
int rank[MAXN];
int stmin[20][MAXN];
int* height=stmin[0];
void Build();
int LCP(int,int);
};
SuffixArray pf,sf;
int n;
int lg[MAXN];
int cnt[MAXN];
char buf[MAXN];
int lcnt[MAXN];
int rcnt[MAXN];
int* x=new int[MAXN];
int* y=new int[MAXN];
int LCP(int,int);
int LCS(int,int);
int main(){
int T;
scanf("%d",&T);
for(int i=2;i<MAXN;i++)
lg[i]=lg[i>>1]+1;
while(T--){
scanf("%s",buf+1);
n=strlen(buf+1);
for(int i=1;i<=n;i++)
pf.s[i]=sf.s[n-i+1]=buf[i];
pf.Build();
sf.Build();
memset(lcnt,0,sizeof(int)*(n+1));
memset(rcnt,0,sizeof(int)*(n+1));
for(int len=1;(len<<1)<=n;len++){
for(int a=1,b;(b=a+len)<=n;a=b){
int p=LCP(a,b);
int s=LCS(a-1,b-1);
if(p+s>=len){
int l=std::max(a-len+1,a-s);
int r=std::min(a-(len-p),a);
++lcnt[l];
--lcnt[r+1];
++rcnt[l+(len<<1)-1];
--rcnt[r+(len<<1)];
}
}
}
for(int i=1;i<=n;i++){
lcnt[i]+=lcnt[i-1];
rcnt[i]+=rcnt[i-1];
}
intEx ans=0;
for(int i=1;i<n;i++)
ans+=1ll*rcnt[i]*lcnt[i+1];
printf("%lld\n",ans);
}
return 0;
}
int LCP(int a,int b){
return pf.LCP(a,b);
}
int LCS(int a,int b){
return sf.LCP(n-a+1,n-b+1);
}
int SuffixArray::LCP(int a,int b){
if(a<1||a>n||b<1||b>n)
return 0;
else if(a==b)
return n-a+1;
else{
a=rank[a];
b=rank[b];
if(a>b)
std::swap(a,b);
int p=lg[b-a];
++a;
return std::min(stmin[p][a],stmin[p][b-(1<<p)+1]);
}
}
void SuffixArray::Build(){
int m=127;
memset(x,0,sizeof(int)*(n+2));
memset(y,0,sizeof(int)*(n+2));
memset(cnt,0,sizeof(int)*(m+1));
for(int i=1;i<=n;i++)
++cnt[x[i]=s[i]];
for(int i=1;i<=m;i++)
cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)
SA[cnt[x[i]]--]=i;
for(int k=1;k<n;k<<=1){
int p=0;
for(int i=n-k+1;i<=n;i++)
y[++p]=i;
for(int i=1;i<=n;i++)
if(SA[i]>k)
y[++p]=SA[i]-k;
memset(cnt,0,sizeof(int)*(m+1));
for(int i=1;i<=n;i++)
++cnt[x[i]];
for(int i=1;i<=m;i++)
cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)
SA[cnt[x[y[i]]]--]=y[i];
std::swap(x,y);
x[SA[1]]=1;
p=1;
for(int i=2;i<=n;i++)
x[SA[i]]=(y[SA[i]]==y[SA[i-1]]&&y[SA[i]+k]==y[SA[i-1]+k])?p:++p;
if(p>=n)
break;
m=p;
}
for(int i=1;i<=n;i++)
rank[SA[i]]=i;
int k=0;
for(int i=1;i<=n;i++){
if(rank[i]==1)
continue;
if(k)
--k;
int j=SA[rank[i]-1];
while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])
++k;
height[rank[i]]=k;
}
for(int i=1;(1<<i)<=n;i++){
for(int j=2;j<=n;j++){
stmin[i][j]=stmin[i-1][j];
if(j+(1<<(i-1))<=n)
stmin[i][j]=std::min(stmin[i][j],stmin[i-1][j+(1<<(i-1))]);
}
}
}

[LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分的更多相关文章
- [bzoj 4650][NOI 2016]优秀的拆分
传送门 Description 如果一个字符串可以被拆分为\(AABB\) 的形式,其中$ A$和 \(B\)是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串\(aabaaba ...
- [LOJ 2134][UOJ 132][BZOJ 4200][NOI 2015]小园丁与老司机
[LOJ 2134][UOJ 132][BZOJ 4200][NOI 2015]小园丁与老司机 题意 给定平面上的 \(n\) 个整点 \((x_i,y_i)\), 一共有两个问题. 第一个问题是从原 ...
- [LOJ 2133][UOJ 131][BZOJ 4199][NOI 2015]品酒大会
[LOJ 2133][UOJ 131][BZOJ 4199][NOI 2015]品酒大会 题意 给定一个长度为 \(n\) 的字符串 \(s\), 对于所有 \(r\in[1,n]\) 求出 \(s\ ...
- [LOJ 2718][UOJ 393][BZOJ 5415][NOI 2018]归程
[LOJ 2718][UOJ 393][BZOJ 5415][NOI 2018]归程 题意 给定一张无向图, 每条边有一个距离和一个高度. 再给定 \(q\) 组可能在线的询问, 每组询问给定一个点 ...
- [LOJ 2721][UOJ 396][BZOJ 5418][NOI 2018]屠龙勇士
[LOJ 2721][UOJ 396][BZOJ 5418][NOI 2018]屠龙勇士 题意 题面好啰嗦啊直接粘LOJ题面好了 小 D 最近在网上发现了一款小游戏.游戏的规则如下: 游戏的目标是按照 ...
- NOI 2016 优秀的拆分 (后缀数组+差分)
题目大意:给你一个字符串,求所有子串的所有优秀拆分总和,优秀的拆分被定义为一个字符串可以被拆分成4个子串,形如$AABB$,其中$AA$相同,$BB$相同,$AB$也可以相同 作为一道国赛题,95分竟 ...
- 【BZOJ 4650】【UOJ #219】【NOI 2016】优秀的拆分
http://www.lydsy.com/JudgeOnline/problem.php?id=4650 http://uoj.ac/problem/219 这里有非常好的题解qwq 接着道题复习一下 ...
- UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组、ST表)
连NOI Day1T1都不会做...看了题解都写不出来还要抄Claris的代码.. 题目链接: (luogu)https://www.luogu.org/problemnew/show/P1117 ( ...
- 字符串(后缀自动机):NOI 2016 优秀的拆分
[问题描述] 如果一个字符串可以被拆分为 AABB 的形式,其中 A 和 B 是任意非空字符串, 则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 aabaabaa,如果令 A = aab, B ...
随机推荐
- WPF (DataGridColumnHeader)实现自义定列头样式 并绑定数据
原文:WPF (DataGridColumnHeader)实现自义定列头样式 并绑定数据 实现功能是这样的 自定义列头 样式 样式里的 数据来源于后台绑定 这篇就说头样式 和头样式数据绑定 思路 1) ...
- C语言中,关于排序的问题(输入n个数,输出最大的那个)
int n,max=0,t; scanf("%d",&n); int a[n],i,k; //这个a[n]必须要在输入n的值之后才能定义,不然定义不成. for(i=1; ...
- Python进阶小结
目录 一.异常TODO 二.深浅拷贝 2.1 拷贝 2.2 浅拷贝 2.3 深拷贝 三.数据类型内置方法 3.1 数字类型内置方法 3.1.1 整型 3.1.2 浮点型 3.2 字符串类型内置方法 3 ...
- Python连载42-异步协程函数
一. asyncio 1.python3.4开始引入标准库之中,内置对异步io的支持 2.asyncio本身是一个消息循环 3.步骤: (1)创建消息循环 (2)把协程导入 (3)关闭 4.举例: ...
- 利用Runtime对Ivar实例变量进行共用的归档和解档方式
一.介绍 在OC中每一个对象持有的变量都是实例变量,实例变量包括成员变量和属性变量,在runtime中用Ivar表示对象的实例变量.其实,runtime源码中可以看到,Ivar也是一个结构体(基本上在 ...
- hibernate的对象/关系映射结果为空,exists查不到值的问题-20190823
1: hibernate的对象/关系映射 情景:在使用@onetotone/@manytonone时关联结果为空 原因:在使用这个注解的时候,默认的时crossjoin(交叉连接),在进行查询时以及排 ...
- MQ的深入理解
MQ的深入理解 MQ的作用: 解耦合,降低系统与系统之间的频繁改动 MQ的特点: 复杂的系统解耦合 (主系统将数据放入mq,子系统需要的时候直接从mq中取出数据)复杂链路的异步调用(将必要的链路执行完 ...
- jQuery 源码分析(十五) 数据操作模块 val详解
jQuery的属性操作模块总共有4个部分,本篇说一下最后一个部分:val值的操作,也是属性操作里最简单的吧,只有一个API,如下: val(vlaue) ;获取匹配元素集合中第一个元素的 ...
- Go template高级用法、深入详解、手册、指南、剖析
入门示例 以下为test.html文件的内容,里面使用了一个template语法{{.}}. <!DOCTYPE html> <html> <head> <m ...
- C# 简单的进程间通信方式
只想实现一个简单的进程建通信,有什么快速实现方式? 1 进程单例需求 / Windows 消息与 IMessageFilter 如果需求是实现一个进程单例,在启动第二个进程时,期望能自动唤起第一个进程 ...