【后缀数组】CF653F Paper task

【题目】
CF
给定一个括号串,问有多少种不同的合法括号子串。 n ≤ 5 × 1 0 5 n\leq 5\times 10^5 n5×105

【解题思路】
首先我们将左括号看作 1 1 1,右括号看作 − 1 -1 1,肯定要处理出一个前缀和数组。

如果不考虑重复,那么我们不妨考虑求以每个位置开头的合法子串个数,即对于每个位置 i i i,我们需要求有多少个 i &lt; j ≤ n , s.t. s j = s i − 1 , ∀ i ≤ k ≤ j , s k ≥ s i − 1 i&lt;j\leq n,\text{s.t.} s_j=s_{i-1},\forall i\leq k \leq j ,s_k\geq s_{i-1} i<jn,s.t.sj=si1,ikj,sksi1

这个问题我们可以先处理出每种前缀和的所有位置,并通过 RMQ \text{RMQ} RMQ维护区间最小值,然后在之前处理出的位置数组中二分判断最大可选的 j j j在哪个位置即可。

现在要求去重,我们可以求出 h e i g h t height height数组,然后 j j j的可选范围就是 s a i + h e i g h t i + 1 ≤ j ≤ n sa_i+height_i+1\leq j\leq n sai+heighti+1jn,除此之外的做法都是一样的。

复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

【参考代码】

#include<bits/stdc++.h>
#define pb push_back
using namespace std;

typedef long long ll;
const int N=1e6+10,M=500001;

namespace SA
{
	int sa[N],rk[N],hi[N];
	int wa[N],wb[N],wx[N],wy[N];
	bool cmp(int *r,int a,int b,int l){return r[a]==r[b] && r[a+l]==r[b+l];}
	void getsa(int *r,int n,int m)
	{
		int *x=wa,*y=wb,*t,i,j,p;
		for(i=0;i<m;++i) wx[i]=0;
		for(i=0;i<n;++i) wx[x[i]=r[i]]++;
		for(i=1;i<m;++i) wx[i]+=wx[i-1];
		for(i=n-1;~i;--i) sa[--wx[x[i]]]=i;
		for(j=1,p=0;p<n;j<<=1,m=p)
		{
			for(p=0,i=n-j;i<n;++i) y[p++]=i;
			for(i=0;i<n;++i) if(sa[i]>=j) y[p++]=sa[i]-j; 
			for(i=0;i<m;++i) wx[i]=0;
			for(i=0;i<n;++i) wx[wy[i]=x[y[i]]]++;
			for(i=1;i<m;++i) wx[i]+=wx[i-1];
			for(i=n-1;~i;--i) sa[--wx[wy[i]]]=y[i];
			for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;++i)
				x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
		} 
	}
	void getheight(int *r,int n)
	{
		int i,j,k=0;
		for(i=1;i<=n;++i) rk[sa[i]]=i;
		for(i=0;i<n;hi[rk[i++]]=k)
			for(k?--k:0,j=sa[rk[i]-1];r[i+k]==r[j+k];++k);
	}
	void adjust(int n)
	{
		for(int i=n;i;--i) ++sa[i],rk[i]=rk[i-1];
		sa[0]=rk[0]=0;
	}
	void output(int n)
	{
		for(int i=0;i<=n;++i) printf("%d ",sa[i]); puts("");
		for(int i=0;i<=n;++i) printf("%d ",rk[i]); puts("");
		for(int i=0;i<=n;++i) printf("%d ",hi[i]); puts("");
	}
	void buildsa(int *r,int n,int m)
	{
		getsa(r,n+1,m);getheight(r,n);adjust(n);
	}
}
using namespace SA;

namespace DreamLolita
{
	ll ans;
	int n,s[N],Log[N],fc[25],h[20][N],sum[N];
	char ch[N];
	vector<int>vec[N];
	void initrmq(int n)
	{
		for(int i=n;i;--i) s[i]=s[i-1],sum[i]=sum[i-1]; s[0]=0;sum[0]=M;
		for(int i=1;i<=n;++i) vec[sum[i]].pb(i);

		fc[0]=1;for(int i=1;i<22;++i)fc[i]=fc[i-1]<<1;
		for(int i=2;i<=n;++i) Log[i]=Log[i>>1]+1;
		for(int i=1;i<=n;++i) h[0][i]=sum[i];
		for(int j=1;j<20;++j) for(int i=1;i+fc[j]-1<=n;++i)
			h[j][i]=min(h[j-1][i],h[j-1][i+fc[j-1]]); 
	}
	int query(int l,int r)
	{
		if(l>r) swap(l,r);
		int t=Log[r-l+1];
		return min(h[t][l],h[t][r-fc[t]+1]);
	}
	void solution()
	{
		scanf("%d%s",&n,ch);
		for(int i=0;i<n;++i) ch[i]=='('?(s[i]=sum[i]=1):(s[i]=2,sum[i]=-1);
		for(int i=1;i<n;++i) sum[i]+=sum[i-1];
		for(int i=0;i<n;++i) sum[i]+=M;
		buildsa(s,n,N);initrmq(n);

		for(int i=1;i<=n;++i)
		{
			int x=sa[i];
			if(s[x]==2) continue;
			int l=x+hi[i],r=n,res=l-1;
			while(l<=r)
			{
				int mid=(l+r)>>1;
				if(query(x,mid)>=sum[x-1]) l=mid+1,res=mid;
				else r=mid-1;
			}

			int t=sum[x-1];
			ans+=upper_bound(vec[t].begin(),vec[t].end(),res)-lower_bound(vec[t].begin(),vec[t].end(),x+hi[i]);
		}//wrong because x+hi[i]->res
		printf("%lld\n",ans);
	}
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("CF653F.in","r",stdin);
	freopen("CF653F.out","w",stdout);
#endif
	DreamLolita::solution();
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值