[冲刺国赛2022] 模拟赛3

字符串

题目描述

我们按照以下的方式定义一个字符串是否合法:

  • 空串是合法的。
  • 如果 \(S\) 是合法的,那么 aSa,bSb,cSc 是合法的。
  • 如果 \(S,T\) 都是合法的,那么 ST 是合法的。
  • 不能被以上方式定义为合法的字符串都是不合法的。

给定一个只包含 a,b,c 的字符串 \(S\),求有多少种方案交换两种不同字符,使得交换之后 \(S\) 合法。

\(|S|\leq 10^5\)

解法

考虑一个串如果不存在相邻相等字符那么就是不合法的,那么判断一个串是否合法的方法就出来了:删去任意相邻相等字符,如果最后可以把串删空,那么这个串就是合法的(哈哈我连这个都想不到

暴力做法可以维护单调栈,假设插入字符 \(c\),如果栈顶元素也是字符 \(c\),那么直接把这两个字符匹配之后弹出栈顶。否则直接向栈中插入字符 \(c\),如果最后栈为空就说明 \(S\) 合法。

考虑优化暴力交换两个元素的过程,直接上猫树分治,每次处理交换点分居左右两侧的情况。考虑现在的分治区间是 \([l,r]\),在分治过程中我们维护 \([1,l)\) 的栈记为 \(L\),再维护 \((r,n]\) 的栈记为 \(R\)(反序,加入方向 \(n\rightarrow r\)

那么左右两边就可以分别处理,考虑暴力找出所有可能的栈,对于左半边(右半边同理),假设替换的点是 \(i\),那么我们把 \([l,i)\) 的字符加入到 \(L\) 中,把 \((i,mid]\) 的字符加入到 \(P\) 中(反序),替换后的字符 \(i\) 加入到 \(L\) 中,现在的问题就是合并这两个栈。

合并一个正序栈和一个反序栈可以直接去掉它们的 \(\tt lcp\),然后把得到的栈 \(\tt hash\) 起来(类似字符串哈希);考虑通过哈希值把左右两部分的栈对应起来,左右栈组合后删空的条件就是左边的正序哈希和右边的反序哈希相等。

合并的时候需要二分 \(\tt lcp\),哈希用 \(O(1)\) 的哈希表,时间复杂度 \(O(|S|\log^2|S|)\)

#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;
const int M = 100005;
#define ull unsigned long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n;char s[M];long long ans;ull pw[M];
__gnu_pbds::gp_hash_table<ull,int> b[3][3];
struct wxk
{
	ull l[M],r[M];int m,s[M];
	void push(int c)
	{
		if(m && s[m]==c) m--;
		else
		{
			s[++m]=c;
			l[m]=l[m-1]*371+c;
			r[m]=r[m-1]+pw[m-1]*c;
		}
	}
	friend pair<ull,ull> operator + (const wxk &u,const wxk &v)
	{
		int l=0,r=min(u.m,v.m),k=0;
		while(l<=r)
		{
			int mid=(l+r)>>1;
			if(u.l[u.m]-u.l[u.m-mid]*pw[mid]
			==v.l[v.m]-v.l[v.m-mid]*pw[mid])
				k=mid,l=mid+1;
			else r=mid-1;
		}
		return {u.l[u.m-k]*pw[v.m-k]+v.r[v.m-k],
		v.l[v.m-k]*pw[u.m-k]+u.r[u.m-k]};
	}
}L,R,P;
void cdq(int l,int r)
{
	if(l==r) return ;
	int mid=(l+r)>>1;
	for(int i=0;i<=2;i++)
		for(int j=0;j<=2;j++) b[i][j].clear();
	//[l,mid]
	P.m=0;
	for(int i=l;i<mid;i++) L.push(s[i]);
	for(int i=mid;i>=l;i--)
	{
		for(int c='a';c<='c';c++) if(c!=s[i])
		{
			L.push(c);
			b[s[i]-'a'][c-'a'][(L+P).first]++;
			L.push(c);
		}
		if(i>l) L.push(s[i-1]),P.push(s[i]);
	}
	//[mid+1,r]
	P.m=0;
	for(int i=r;i>mid+1;i--) R.push(s[i]);
	for(int i=mid+1;i<=r;i++)
	{
		for(int c='a';c<='c';c++) if(c!=s[i])
		{
			R.push(c);
			ans+=b[c-'a'][s[i]-'a'][(P+R).second];
			R.push(c);
		}
		if(i<r) R.push(s[i+1]),P.push(s[i]);
	}
	//
	for(int i=r;i>mid;i--) R.push(s[i]);
	cdq(l,mid);
	for(int i=mid+1;i<=r;i++) R.push(s[i]);
	for(int i=l;i<=mid;i++) L.push(s[i]);
	cdq(mid+1,r);
	for(int i=mid;i>=l;i--) L.push(s[i]);
}
signed main()
{
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	scanf("%s",s+1);n=strlen(s+1);
	pw[0]=1;
	for(int i=1;i<=n;i++) pw[i]=pw[i-1]*371;
	cdq(1,n);
	printf("%lld\n",ans);
}

posted @ 2022-05-30 17:02  C202044zxy  阅读(189)  评论(0编辑  收藏  举报