[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 ... 
随机推荐
- 简单App项目的运行
			我和我的同伴两个人从网上下载一个仿微信登录源码,导入到eclipse中,两人结队练习这个源代码的操作, 运行项目到Android虚拟器上,显示效果为 我们还运行这个项目到手机设备上,显示效果为 接 ... 
- php精确计算
			php BC高精确度函数库 结果: php一般的取余 只是除以整数 bc精度取余 精确到了小数 
- Mybatis关联查询之三
			MyBatis的关联查询之自关联 自关联 一.entity实体类 public class City { private Integer cid; private String cname; priv ... 
- spring cloud 2.x版本 Config配置中心教程
			前言 本文采用Spring cloud本文为2.1.8RELEASE,version=Greenwich.SR3 本文基于前面的文章eureka-server的实现. 参考 eureka-server ... 
- Unity TextMeshPro替代Text组件创建简体中文字体纹理集
			Unity原生的Text组件有一个毛病,只要文本放大字体放大就会有毛边或锯齿,一个更好的解决方案是用TextMeshPro替代ugui中的Text组件. TMPro采用SDF文字渲染技术,可以使文字放 ... 
- Maven的assembly插件在linux启动卡住Starting the localhost.localdomain
			1.今天在测试assembly的时候,在Linux虚拟机,内存配置为512mb,然后开始在Linux上运行assembly的时候就会一直卡住 2.停止运行后,查看了下日志 [root@localho ... 
- 在Asp.net Razor Pages/MVC程序中集成Blazor
			今天试了一下在Asp.net core Razor Pages/MVC程序中集成Blazor(Server-side),还是可以完美整合的,这里以Razor Pages为例(.net core 3.1 ... 
- NetCore 下使用 DataTable  以及可视化工具
			DtatTable 在命名空间System.Data下,NetCore2.0及以上支持.但是2017DataTable没有可视化工具,我也没有深研究直接下载的VS2019.然后在网上早了个SQLHel ... 
- Zipkin客户端链路追踪源码解析
			我们知道,Zipkin这个工具可以帮助我们收集分布式系统中各个系统之间的调用连关系,而且除了Servlet之外还能收集:MQ.线程池.WebSocket.Feign.Hystrix.RxJava.We ... 
- python获得多个输入值
			我们都知道python的input()函数是以字符串的形式输入的,这就产生了一个问题:当我们在一行内输入多个数值时,input()不会去判断输入元素个数,它只管把这行输入以字符串的形式输入,因此我们要 ... 
