题目链接
题目大意
一个字符串T由’A’,’B’,’C’,’?’组成,其中’?’可以变成’A’,’B’,’C’,现在若
Ti=′A′
T
i
=
′
A
′
Tj=′B′
T
j
=
′
B
′
Ti=′C′
T
i
=
′
C
′
且
1<=i< j< k<=|S|,那么该三元组(i,j,k)满足条件,求所有满足条件的三元组组数?由于答案很大,所以最后答案要对
109+7
10
9
+
7
取模
样例输入1:
A??C
样例输出1:
8
样例输入2:
ABCBC
样例输出2:
3
样例输入3:
????C?????B??????A???????
样例输出3:
979596887
数据范围
3≤|S|≤105 3 ≤ | S | ≤ 10 5
分析
当时考试时也是极为懵逼,最后自己离5分钟结束做出来了
这道题题目最后说是什么DP,然而我直接用了简单的数学分析后设计了O(n)的算法
对于没有’?’
这种情况我也想了很久….
其实我们可以发现,这种情况中‘B’的位置是比‘A’‘C’两个字母重要的,因为你一旦确定了一个’B’的位置,那个位置左边的’A’的数量,和那个位置右边的’C’的数量都可以确定,我们就可以用前缀和统计’A’,后缀和统计’C’,我们记当前位置为i,i位置左边的’A’的数量A[i-1],i位置右边的’C’的数量C[i+1],那么对于每一个’B’,包含当前’B’的总方案数就是:A[i-1]*C[i+1]
那么总的方案数就是:
∑ni=1A[i−1]∗C[i+1](s[i]==′B′)
∑
i
=
1
n
A
[
i
−
1
]
∗
C
[
i
+
1
]
(
s
[
i
]
=
=
′
B
′
)
相信这个还是很好理解的
加上’?’
这种情况相比上面就是多了几种情况和多了两个统计:
此时你是不仅仅对于每个’B’来进行类似于上面的操作,你还要对于每个问号这样操作,因为‘?’可以变成’B’
你对于每个位置i除了统计A[i],C[i]后还要统计这个位置之前(包含这个位置)的’?’个数m[i],这个位置之后(包含这个位置)的’?’个数n[i]
①’A’<–>’C’
这种情况时’?’对“ABC”并没有影响,而问号的所有可能方案为为
3m[i−1]+n[i+1]
3
m
[
i
−
1
]
+
n
[
i
+
1
]
,所以此时答案为:
A[i−1]×C[i+1]×3m[i−1]+n[i+1]
A
[
i
−
1
]
×
C
[
i
+
1
]
×
3
m
[
i
−
1
]
+
n
[
i
+
1
]
②‘A’<–>’?’
此时你只需要在后面n[i+1]个’?’中找出一个来当作’C’,剩下
m[i−1]+n[i+1]−1
m
[
i
−
1
]
+
n
[
i
+
1
]
−
1
个问号和’C’对“ABC”并没有影响所以此时答案为:
n×A[i−1]×3m[i−1]+n[i+1]−1
n
×
A
[
i
−
1
]
×
3
m
[
i
−
1
]
+
n
[
i
+
1
]
−
1
③’?’<–>’C’
与②情况大同
此时答案为:
m×C[i−1]×3m[i−1]+n[i+1]−1
m
×
C
[
i
−
1
]
×
3
m
[
i
−
1
]
+
n
[
i
+
1
]
−
1
④’?’<–>?’
你要从前面m[i-1]个’?’中选一个出来当作’A’,从后面n[i+1]个’?’中选出一个出来当作’C’,剩下的问号就对“ABC”有没有影响了
此时答案为:
m×n×3m[i−1]+n[i+1]−2
m
×
n
×
3
m
[
i
−
1
]
+
n
[
i
+
1
]
−
2
那么对于一个合法的位置(s[i]为’B’或’?’)的答案为:
A[i−1]×C[i+1]×3m[i−1]+n[i+1]+n×A[i−1]×3m[i−1]+n[i+1]−1+m×C[i−1]×3m[i−1]+n[i+1]−1+m×n×3m[i−1]+n[i+1]−2
A
[
i
−
1
]
×
C
[
i
+
1
]
×
3
m
[
i
−
1
]
+
n
[
i
+
1
]
+
n
×
A
[
i
−
1
]
×
3
m
[
i
−
1
]
+
n
[
i
+
1
]
−
1
+
m
×
C
[
i
−
1
]
×
3
m
[
i
−
1
]
+
n
[
i
+
1
]
−
1
+
m
×
n
×
3
m
[
i
−
1
]
+
n
[
i
+
1
]
−
2
那么最终的答案就是这写答案之和.
温馨提示1:开LL
温馨提示2:
3k
3
k
预处理出来,不要每次算快速幂!!很慢…
温馨提示3:注意每次乘时的取模,每次相加时的取模!!!
代码
#include<set>
#include<map>
#include<ctime>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
LL read(){
LL f=1,x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
return x*f;
}
#define MAXN 100000
#define INF 0x3f3f3f3f
#define MOD int(1e9+7)
LL Pow[MAXN+5]={1};
LL A[MAXN+5],C[MAXN+5],q1[MAXN+5],q2[MAXN+5];
char str[MAXN+5];
int main(){
scanf("%s",str+1);
int n=strlen(str+1);
for(int i=1;i<=n;i++)//预处理出3^k
Pow[i]=(Pow[i-1]*3)%MOD;
for(int i=1;i<=n;i++){//处理出对于i位置之前的'A'和'?'
A[i]=A[i-1],q1[i]=q1[i-1];
if(str[i]=='A')//均包含第i个位置
A[i]++;
if(str[i]=='?')
q1[i]++;
}
for(int i=n;i>=1;i--){//处理出对于i位置之后的'C'和'?'
C[i]=C[i+1],q2[i]=q2[i+1];
if(str[i]=='C')//均包含第i个位置
C[i]++;
if(str[i]=='?')
q2[i]++;
}
LL ans=0,t1,t2,t3,t4;
for(int i=1;i<=n;i++)
if(str[i]=='B'||str[i]=='?'){//数学计数
t1=((A[i-1]*C[i+1])%MOD*Pow[q1[i-1]+q2[i+1]])%MOD;
t2=((A[i-1]*q2[i+1])%MOD*Pow[q1[i-1]+q2[i+1]-1])%MOD;
t3=((C[i+1]*q1[i-1])%MOD*Pow[q1[i-1]+q2[i+1]-1])%MOD;
t4=((q1[i-1]*q2[i+1])%MOD*Pow[q1[i-1]+q2[i+1]-2])%MOD;
ans=(ans+((t1+t2)%MOD+(t3+t4)%MOD)%MOD)%MOD;//注意答案不要溢出
}
printf("%lld\n",ans);
return 0;
}