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 n≤106
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 1∼i中含 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,0−sumj,0=sumi,1−sumj,1=sumi,2−sumj,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,0−sumj,0=sumi,1−sumj,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,1−sumj,1=sumi,2−sumj,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,0−sumi,1=sumj,0−sumj,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,1−sumi,2=sumj,1−sumj,2
设
p
i
=
s
u
m
i
,
0
−
s
u
m
i
,
1
p_i=sum_{i,0}-sum_{i,1}
pi=sumi,0−sumi,1
q
i
=
s
u
m
i
,
1
−
s
u
m
i
,
2
q_i=sum_{i,1}-sum_{i,2}
qi=sumi,1−sumi,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=(i−j+1)相同序列中,这段对答案的贡献就是 k ( k − 1 ) 2 \frac{k(k-1)}2 2k(k−1)
于是问题得到解决,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
补充:
- h a s h hash hash函数的构造方法:考虑到 0 ≤ p i , q i ≤ n 0\leq p_i,q_i\leq n 0≤pi,qi≤n,直接令 h i = n p i + q i h_i=np_i+q_i hi=npi+qi即可,这样一定不会有哈希冲突
- 下列代码中 i i i对应的是左端点, j j j是右端点,各位不要理解错了
- 注意判断式中的 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题解后,看到这道题直接就一眼正解了,看来题目的狩猎还是非常重要的