[CF1043G]Speckled Band

题意:给定字符串$s_{1\cdots n}$,多次询问它的一个子串$s_{l\cdots r}$能否被切割成多个部分,使得至少有一个部分出现两次,且切出来的本质不同字符串数最少

做一道题学了两个算法...

首先是一个找出所有形如$aa$的子串的算法,来自于这篇论文,好像没有名字?那就叫它main and lorentz算法吧...

整个算法基于分治,每次找出跨越$s_{l\cdots mid},s_{mid+1\cdots r}$的那些$aa$串,不妨先找那些对称轴在右边的$aa$串,之后反过来找即可

我们要找连接$u,v$两个串后新形成的对称轴在$v$内的$aa$串,先用扩展kmp预处理出$ls_i=\left\lvert\text{lcs}(u,v_{1\cdots i})\right\rvert,lp_i=\left\lvert\text{lcp}(v,v_{i\cdots|v|})\right\rvert$

如果存在长度为$n$的以$v_i$结尾的对称轴在$v$内的$aa$串,那么有$n\leq i\lt2n,ls_n\geq2n-i,lp_{n+1}\geq i-n$,也就是说$2n-ls_n\leq i\leq\min(2n-1,n+lp_{n+1})$,所以枚举$n$,并更新对应的$i$即可

如果是求每个位置开始的最长/最短$aa$串,那么要用线段树来辅助更新,时间复杂度$O(n\log^2n)$

如果是计数,这样会重复计算那些对称轴恰好在$u,v$连接处的$aa$串,减去相应的$\sum\limits_i[ls_i=i]$即可,时间复杂度$O(n\log n)$

然后是一个查询子串border的算法,想要好复杂度的可以去看策爷ppt,这里给一个简单好写的$O(\sqrt n)$后缀数组做法

对于子串$s_{l\cdots r}$,如果要找最短的border,首先暴力找长度为$1\cdots\sqrt n$的border,如果找不到且border存在,那么这个长度$\gt\sqrt n$的border$i$($s_{i\cdots r}=s_{l\cdots l+r-i+1}$)一定满足$i$和$l$在后缀数组中的距离$\leq\sqrt n$

证明:考虑反证,如果距离$\gt\sqrt n$,那么说明有$\gt\sqrt n$个长度$\gt\sqrt n$的不同位置的子串相等,其中一定会有重叠,也就是说我们找到了更短的border

然后是这道题,如果能在子串中找到两个相同字符,那么切割方案形如$?a?a?$,也就是说答案最多是$4$,接下来就是分类讨论了

设$lt_i$为最小的$r$使得$s_{i\cdots r}$是$aa$串,$rt_i$为最大的$l$使得$s_{l\cdots i}$是$aa$串,这两个数组可以用main and lorentz算法预处理

$-1$:没有相同字符时就无解,只有当$r-l+1\leq26$时才会发生,直接扫一遍即可

$1$:整个串形如$aa\cdots a$,如果我们找最长的$a$,那么$\frac{r-l+1}{|a|}$就是质数,所以直接枚举$r-l+1$的所有质因子即可

$2$:整个串形如$aab$或$baa$或$aba$,第一种就是$lt_l\leq r$,第二种就是$rt_r\geq l$,第三种就是问这个子串是否有长度$\leq\frac{r-l+1}2$的border

$3$:整个串形如$abac,baca,baac$,容易看出如果存在前两种方案,那么肯定有$|a|=1$,第一种就是判断$s_l$是否在$s_{l+1\cdots r}$中出现,第二种就是判断$s_r$是否在$s_{l\cdots r-1}$中出现,用前缀和即可,第三种的条件就是存在$l\leq i\leq r$使得$lt_i\leq r$,用ST表预处理得到$lt_i$的区间最小值即可

剩下的情况就是$4$了

说起来挺简单,写起来还是挺长的...

UPD:我就是个智障,这个AA串直接用[NOI2016]优秀的拆分的方法求就可以了...

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
using namespace std;
const int inf=2147483647;
char s[200010];
int n,sq;
namespace ex{
	char s[200010],t[200010];
	int nex[200010],ex[200010],n,m;
	void getnex(){
		int i,a,p;
		m=strlen(t+1);
		memset(nex,0,(m+1)<<2);
		a=p=0;
		nex[1]=m;
		for(i=2;i<=m;i++){
			if(i+nex[i-a+1]>=p){
				for(p=max(p,i);p<=m&&t[p]==t[p-i+1];p++);
				nex[i]=p-i;
				a=i;
			}else
				nex[i]=nex[i-a+1];
		}
	}
	void getex(){//ex[i]=lcp(s[i],t)
		int i,a,p;
		getnex();
		n=strlen(s+1);
		a=p=0;
		for(i=1;i<=n;i++){
			if(i+nex[i-a+1]>=p){
				for(p=max(p,i);p<=n&&p-i+1<=m&&s[p]==t[p-i+1];p++);
				ex[i]=p-i;
				a=i;
			}else
				ex[i]=nex[i-a+1];
		}
	}
}
int*po;
struct seg{
	int mn[800010];
	void build(int l,int r,int x){
		mn[x]=inf;
		if(l==r)return;
		int mid=(l+r)>>1;
		build(l,mid,x<<1);
		build(mid+1,r,x<<1|1);
	}
	void fmin(int x,int v){
		if(v<mn[x])mn[x]=v;
	}
	void pushdown(int x){
		if(mn[x]!=inf){
			fmin(x<<1,mn[x]);
			fmin(x<<1|1,mn[x]);
			mn[x]=inf;
		}
	}
	void modify(int L,int R,int v,int l=1,int r=n,int x=1){
		if(L<=l&&r<=R)return fmin(x,v);
		pushdown(x);
		int mid=(l+r)>>1;
		if(L<=mid)modify(L,R,v,l,mid,x<<1);
		if(mid<R)modify(L,R,v,mid+1,r,x<<1|1);
	}
	void dfs(int l,int r,int x){
		if(l==r){
			po[l]=mn[x];
			return;
		}
		pushdown(x);
		int mid=(l+r)>>1;
		dfs(l,mid,x<<1);
		dfs(mid+1,r,x<<1|1);
	}
}sl,sr;
namespace mainlo{
	int ls[200010],lp[200010],pos;
	void newright(char*a,int n,char*b,int m,bool f){
		memcpy(ex::t+1,b+1,m);
		ex::t[m+1]=0;
		ex::getnex();
		memcpy(lp,ex::nex,(m+1)<<2);
		lp[m+1]=0;
		reverse(a+1,a+n+1);
		reverse(b+1,b+m+1);
		memcpy(ex::s+1,b+1,m);
		ex::s[m+1]=0;
		memcpy(ex::t+1,a+1,n);
		ex::t[n+1]=0;
		ex::getex();
		memcpy(ls,ex::ex,(m+1)<<2);
		reverse(ls+1,ls+m+1);
		int i,st,en;
		for(i=1;i<=m;i++){
			st=2*i-ls[i];
			en=min(2*i-1,i+lp[i+1]);
			if(st<=en){
				if(f){
					sl.modify(pos+st-2*i+1,pos+en-2*i+1,i*2);
					sr.modify(pos+st,pos+en,i*2);
				}else{
					sl.modify(pos-en,pos-st,i*2);
					sr.modify(pos-en+i*2-1,pos-st+i*2-1,i*2);
				}
			}
		}
	}
	void solve(int l,int r){
		if(l==r)return;
		int mid=(l+r)>>1,ln=mid-l+1,rn=r-mid;
		pos=mid;
		newright(s+l-1,ln,s+mid,rn,1);
		pos=mid+1;
		newright(s+mid,rn,s+l-1,ln,0);
		solve(l,mid);
		solve(mid+1,r);
	}
}
namespace sa{
	int rk[400010],sa[200010],h[200010],c[200010],mn[200010][18],lg[200010];
	struct pr{
		int c[2],i;
		pr(int a=0,int b=0,int d=0):c{a,b},i(d){}
	}p[200010],q[200010];
	bool operator!=(pr a,pr b){return a.c[0]!=b.c[0]||a.c[1]!=b.c[1];}
	void sort(int f){
		int m,i;
		memset(c,0,sizeof(c));
		for(i=1,m=0;i<=n;i++){
			c[p[i].c[f]]++;
			m=max(m,p[i].c[f]);
		}
		for(i=1;i<=m;i++)c[i]+=c[i-1];
		for(i=n;i>0;i--)q[c[p[i].c[f]]--]=p[i];
		memcpy(p,q,sizeof(q));
	}
	void suf(){
		int i,j,l,m;
		for(i=1;i<=n;i++)rk[i]=s[i]-'a'+1;
		for(l=1;l<=n;l<<=1){
			for(i=1;i<=n;i++)p[i]=pr(rk[i],rk[i+l],i);
			sort(1);
			sort(0);
			for(i=1,m=0;i<=n;i++){
				if(p[i]!=p[i-1])m++;
				rk[p[i].i]=m;
			}
		}
		for(i=1;i<=n;i++)sa[rk[i]]=i;
		for(i=1,l=0;i<=n;i++){
			if(l)l--;
			while(s[i+l]==s[sa[rk[i]-1]+l])l++;
			h[rk[i]]=l;
		}
		for(i=1;i<=n;i++)mn[i][0]=h[i];
		for(j=1;j<18;j++){
			for(i=1;i+(1<<j)-1<=n;i++)mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
		}
		for(i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
	}
	int query(int l,int r){
		int k=lg[r-l+1];
		return min(mn[l][k],mn[r-(1<<k)+1][k]);
	}
	int lcp(int x,int y){
		if(rk[x]>rk[y])swap(x,y);
		return query(rk[x]+1,rk[y]);
	}
	int border(int l,int r){
		int i,s=inf;
		for(i=1;i<=sq&&i<r-l+1;i++){
			if(lcp(l,r-i+1)>=i)return i;
		}
		for(i=max(1,rk[l]-sq+1);i<=min(n,rk[l]+sq-1);i++){
			if(l<sa[i]&&sa[i]<=r&&lcp(l,sa[i])>=r-sa[i]+1)s=min(s,r-sa[i]+1);
		}
		return s==inf?inf/2:s;
	}
}
int lt[200010],rt[200010];
bool vis[30];
bool checkno(int l,int r){
	if(r-l+1>26)return 0;
	memset(vis,0,sizeof(vis));
	for(int i=l;i<=r;i++){
		if(vis[s[i]-'a'])return 0;
		vis[s[i]-'a']=1;
	}
	return 1;
}
vector<int>pd[200010];
bool check1(int l,int r){
	for(int&t:pd[r-l+1]){
		if(sa::lcp(l,l+(r-l+1)/t)>=r-l+1-(r-l+1)/t)return 1;
	}
	return 0;
}
bool check2(int l,int r){
	return lt[l]<=r||rt[r]>=l||sa::border(l,r)*2<=r-l+1;
}
int sum[200010][26];
int get(int l,int r,int c){
	return sum[r][c-'a']-sum[l-1][c-'a'];
}
int mn[200010][18],lg[200010];
int qmin(int l,int r){
	int k=lg[r-l+1];
	return min(mn[l][k],mn[r-(1<<k)+1][k]);
}
bool check3(int l,int r){
	return get(l+1,r,s[l])||get(l,r-1,s[r])||qmin(l,r)<=r;
}
int query(int l,int r){
	if(checkno(l,r))return-1;
	if(check1(l,r))return 1;
	if(check2(l,r))return 2;
	if(check3(l,r))return 3;
	return 4;
}
int main(){
	int m,i,j,l,r;
	scanf("%d%s",&n,s+1);
	sq=sqrt(n);
	sl.build(1,n,1);
	sr.build(1,n,1);
	mainlo::solve(1,n);
	po=lt;
	sl.dfs(1,n,1);
	po=rt;
	sr.dfs(1,n,1);
	sa::suf();
	for(i=1;i<=n;i++)lt[i]=lt[i]==inf?n+1:i+lt[i]-1;
	for(i=1;i<=n;i++)rt[i]=rt[i]==inf?0:i-rt[i]+1;
	for(i=2;i<=n;i++){
		if(pd[i].empty()){
			for(j=i;j<=n;j+=i)pd[j].push_back(i);
		}
	}
	for(i=1;i<=n;i++){
		memcpy(sum[i],sum[i-1],sizeof(sum[i-1]));
		sum[i][s[i]-'a']++;
	}
	for(i=1;i<=n;i++)mn[i][0]=lt[i];
	for(j=1;j<18;j++){
		for(i=1;i+(1<<j)-1<=n;i++)mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
	}
	for(i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
	scanf("%d",&m);
	while(m--){
		scanf("%d%d",&l,&r);
		printf("%d\n",query(l,r));
	}
}

转载于:https://www.cnblogs.com/jefflyy/p/9905117.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值