JZOJ 3798.【NOIP2014模拟8.22】临洮巨人

D e s c r i p t i o n Description Description

给定一个长度为 n n n的字符串,要求求出包含 A , B , C A,B,C A,B,C数目相同的子串个数

数据范围: n ≤ 1 0 6 n\leq 10^6 n106


S o l u t i o n Solution Solution

枚举两个端点,这样就唯一确定了一个区间,然后 O ( n ) O(n) O(n)判断,这样做是 O ( n 3 ) O(n^3) O(n3)的,可以拿到30 p t s pts pts

将上述算法的判断改成前缀和,即

s u m i , 0 / 1 / 2 sum_{i,0/1/2} sumi,0/1/2表示 1 ∼ i 1\sim i 1i中含 A / B / C A/B/C A/B/C的个数,则一个串合法,当且仅当

s u m i , 0 − s u m j , 0 = s u m i , 1 − s u m j , 1 = s u m i , 2 − s u m j , 2 ( j < i ) sum_{i,0}-sum_{j,0}=sum_{i,1}-sum_{j,1}=sum_{i,2}-sum_{j,2}(j<i) sumi,0sumj,0=sumi,1sumj,1=sumi,2sumj,2(j<i)

[ j + 1 , i ] [j+1,i] [j+1,i]区间合法

这样就可以 O ( n 2 ) O(n^2) O(n2)搞,可以拿到70分的高分

上述算法的判断显然已经最优,唯一的瓶颈就是枚举区间,我们考虑只枚举右端点,然后计算合法的左端点数量

考虑将连等条件转换为两个同时成立的条件,即将上述判断式转换为

s u m i , 0 − s u m j , 0 = s u m i , 1 − s u m j , 1 sum_{i,0}-sum_{j,0}=sum_{i,1}-sum_{j,1} sumi,0sumj,0=sumi,1sumj,1

s u m i , 1 − s u m j , 1 = s u m i , 2 − s u m j , 2 sum_{i,1}-sum_{j,1}=sum_{i,2}-sum_{j,2} sumi,1sumj,1=sumi,2sumj,2

注意到这里 i i i j j j相关比较难搞,考虑把它们分开,即移项:

s u m i , 0 − s u m i , 1 = s u m j , 0 − s u m j , 1 sum_{i,0}-sum_{i,1}=sum_{j,0}-sum_{j,1} sumi,0sumi,1=sumj,0sumj,1

s u m i , 1 − s u m i , 2 = s u m j , 1 − s u m j , 2 sum_{i,1}-sum_{i,2}=sum_{j,1}-sum_{j,2} sumi,1sumi,2=sumj,1sumj,2


p i = s u m i , 0 − s u m i , 1 p_i=sum_{i,0}-sum_{i,1} pi=sumi,0sumi,1
q i = s u m i , 1 − s u m i , 2 q_i=sum_{i,1}-sum_{i,2} qi=sumi,1sumi,2

则条件转换成为
p i = p j p_i=p_j pi=pj q i = q j ( j < i ) q_i=q_j(j<i) qi=qj(j<i)的数对个数

也就是说对于每一个确定的 i i i,需要找前面所有等于它的 j j j,似乎有些麻烦(当然也可以做)

考虑如何将两个条件同时判断
一个显然的想法是 h a s h hash hash,即令 h i = h a s h ( p i , q i ) h_i=hash(p_i,q_i) hi=hash(pi,qi),这样我们就转换成为

对于每一个确定的 h i h_i hi,求 h j = h i ( j < i ) h_j=h_i(j<i) hj=hi(j<i)的数对个数,显然我们直接一个 s t a b l e _ s o r t stable\_sort stable_sort这样在一段连续的长度为 k = ( i − j + 1 ) k=(i-j+1) k=(ij+1)相同序列中,这段对答案的贡献就是 k ( k − 1 ) 2 \frac{k(k-1)}2 2k(k1)

于是问题得到解决,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

补充:

  1. h a s h hash hash函数的构造方法:考虑到 0 ≤ p i , q i ≤ n 0\leq p_i,q_i\leq n 0pi,qin,直接令 h i = n p i + q i h_i=np_i+q_i hi=npi+qi即可,这样一定不会有哈希冲突
  2. 下列代码中 i i i对应的是左端点, j j j是右端点,各位不要理解错了
  3. 注意判断式中的 j j j是包括0的,因为左端点可以是1,所以我们新添加进去一个0即可,即 h [ + + n ] = 0 h[++n]=0 h[++n]=0
C o d e Code Code
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define N 1000010
using namespace std;char s[N];
int n;
LL sum[N][3],h[N],ans;
signed main()
{
	scanf("%s",s+1);
	n=strlen(s+1);
	for(register int i=1;i<=n;i++)
	{
		sum[i][0]=sum[i-1][0]+(s[i]=='A');
		sum[i][1]=sum[i-1][1]+(s[i]=='B');
		sum[i][2]=sum[i-1][2]+(s[i]=='C');
		h[i]=1ll*n*(sum[i][1]-sum[i][0])+(sum[i][2]-sum[i][1]);
	}
	h[++n]=0;
	stable_sort(h+1,h+1+n);
	for(register int i=1,j;i<=n;i=j+1)
	{
		for(j=i;j<n&&h[i]==h[j+1];j++);
		ans+=(j-i+1)*(j-i)/2;
	}
	printf("%lld",ans);
}

昨天看了 z y c zyc zyc的D1T2题解后,看到这道题直接就一眼正解了,看来题目的狩猎还是非常重要的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值