【题解】P1117 优秀的拆分

题目大意

给定字符串,求所有的AABB的个数和。(字符串长度小于等于30000)

题解

写篇题解记录一下这个经典trick。
首先如果我们钦定AA中A的长度为len,将序列按len个依次分块,现在我们有nlen块,那么每个A一定至少和一个分割点相交。
那么我们可以处理出每块与下一块的最长公前缀和最长公共后缀,于是可以快速处理出相邻两块的信息。
对于此题,要求一个AA和一个BB拼起来,我们可以通过上面的方法处理出AA的结尾在哪些区间,差分处理一下即可求出答案。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
inline int read() {
#define num ch-'0'
	char ch;
	bool flag=0;
	int res;
	while(!isdigit(ch=getc()))
		(ch=='-')&&(flag=true);
	for(res=num; isdigit(ch=getc()); res=res*10+num);
	(flag)&&(res=-res);
#undef num
	return res;
}
char sr[1<<21],z[20];
int C=-1,Z;
inline void Ot() {
	fwrite(sr,1,C+1,stdout),C=-1;
}
inline void print(ll x) {
	if(C>1<<20)Ot();
	if(x<0)sr[++C]=45,x=-x;
	while(z[++Z]=x%10+48,x/=10);
	while(sr[++C]=z[Z],--Z);
	sr[++C]='\n';
}
const int N=30005,mod=3e7+7;
char s[N];
int n;
ll hsh[N],mo[N],u[N],v[N],ans;
inline ll gethash(int l,int r) {
	ll now=hsh[l]-hsh[r]*mo[r-l];
	now%=mod,now+=mod,now%=mod;
	return now;
}
int main() {
	int T=read();
	mo[0]=1;
	for(int i=1; i<=30000; ++i) mo[i]=mo[i-1]*31%mod;
	while(T--) {
		n=0;
		char ch;
		while((ch=getc())!='\n') s[++n]=ch;
		memset(u,0,sizeof(u)),memset(v,0,sizeof(v));
		hsh[n+1]=0;
		for(int i=n; i; --i) (hsh[i]=hsh[i+1]*31+s[i]-'a'+1)%=mod;
		for(int L=1; L*2<=n; ++L) {
			for(int i=L<<1; i<=n; i+=L) {
				if(s[i]!=s[i-L]) continue;
				int l=1,r=L,last=i-L,pos=0;
				while(l<=r) {
					int mid=l+r>>1;
					if(gethash(last-mid+1,last+1)==gethash(i-mid+1,i+1)) pos=mid,l=mid+1;
					else r=mid-1;
				}
				int head=i-pos+1;
				l=1,r=L,pos=0;
				while(l<=r) {
					int mid=l+r>>1;
					if(gethash(last,last+mid)==gethash(i,i+mid)) pos=mid,l=mid+1;
					else r=mid-1;
				}
				int tail=i+pos-1;
				head=max(head+L-1,i);
				tail=min(tail,i+L-1);
				if(head<=tail) {
					++u[head-2*L+1],--u[tail+1-2*L+1];
					++v[head],--v[tail+1];
				}
			}
		}
		ans=0;
		for(int i=1; i<=n; ++i) u[i]+=u[i-1],v[i]+=v[i-1];
		for(int i=1; i<n; ++i) ans+=v[i]*u[i+1];
		print(ans);
	}
	Ot();
	return 0;
}

(代码是我自己写的吗?)

posted @   flywatre  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示