NOI2016 优秀的拆分

优秀的拆分

如果一个字符串可以被拆分为 \(\text{AABB}\) 的形式,其中 \(\text{A}\)\(\text{B}\) 是任意非空字符串,则我们称该字符串的这种拆分是优秀的。
例如,对于字符串 $ \texttt{aabaabaa} $ ,如果令 \(\text{A}=\texttt{aab}\)\(\text{B}=\texttt{a}\),我们就找到了这个字符串拆分成 \(\text{AABB}\) 的一种方式。

一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。
比如我们令 \(\text{A}=\texttt{a}\)\(\text{B}=\texttt{baa}\),也可以用 \(\text{AABB}\) 表示出上述字符串;但是,字符串 \(\texttt{abaabaa}\) 就没有优秀的拆分。

现在给出一个长度为 \(n\) 的字符串 \(S\),我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。

以下事项需要注意:

  1. 出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。
  2. 在一个拆分中,允许出现 \(\text{A}=\text{B}\)。例如 \(\texttt{cccc}\) 存在拆分 \(\text{A}=\text{B}=\texttt{c}\)
  3. 字符串本身也是它的一个子串。

对于全部的测试点,\(1 \leq T \leq 10, \ n \leq 30000\)

题解

等价于对每个 \(i\) 求出 \(Pre_i\)\(Suf_i\),表示 \(S[1 : i]\) 的 AA 型后缀个数以及 \(S[i : |S|]\) 的 AA 型前缀个数,那么答案显然就是 \(\sum^n_{i=1} Pre_iSuf_{i+1}\)

枚举一下 |A|,把 S 按 |A| 切段,对每两段去讨论一下,可以发现只要求 lcp 就行了

https://blog.csdn.net/qq_40512553/article/details/78915344

时间复杂度 \(O(n \ln n \log n)\)

CO int N=3e4+10;
CO uint128 B=131;
uint128 pw[N],val[N];
char str[N];
int pre[N],suf[N];

IN uint128 calc(int l,int r){
	return val[r]-val[l-1]*pw[r-l+1];
}
void real_main(){
	scanf("%s",str+1);
	int n=strlen(str+1);
	for(int i=1;i<=n;++i) val[i]=val[i-1]*B+str[i];
	fill(pre+1,pre+n+1,0),fill(suf+1,suf+n+1,0);
	for(int L=1;2*L<=n;++L)for(int i=L+L;i<=n;i+=L){
		int p=i-L;
		if(str[i]!=str[p]) continue;
		int lcs=0;
		{
			int l=1,r=L;
			while(l<r){
				int mid=(l+r+1)>>1;
				if(calc(p-mid+1,p)==calc(i-mid+1,i)) l=mid;
				else r=mid-1;
			}
			lcs=l;
		}
		int lcp=0;
		{
			int l=1,r=min(L,n-i+1);
			while(l<r){
				int mid=(l+r+1)>>1;
				if(calc(p,p+mid-1)==calc(i,i+mid-1)) l=mid;
				else r=mid-1;
			}
			lcp=l;
		}
		if(lcs+lcp>L){
			++pre[i-lcs+L],--pre[i+lcp];
			++suf[p-lcs+1],--suf[p+lcp-L+1];
		}
	}
	for(int i=1;i<=n;++i) pre[i]+=pre[i-1],suf[i]+=suf[i-1];
	int64 ans=0;
	for(int i=1;i<n;++i) ans+=(int64)pre[i]*suf[i+1];
	printf("%lld\n",ans);
}
int main(){
	pw[0]=1;
	for(int i=1;i<N;++i) pw[i]=pw[i-1]*B;
	for(int T=read<int>();T--;) real_main();
	return 0;
}

unsigned long long会被卡,最后一个测试点的最后一组数据就是专门卡它的。

posted on 2020-02-20 21:52  autoint  阅读(132)  评论(0编辑  收藏  举报

导航