题目链接:http://codeforces.com/contest/1426/problem/F
描述:
给出一个由a,b,c,?组成的字符串S,?可以变为a,b,c其中任何一个,求在s的所有可能情况中会有多少子序列abc。
3≤n≤200000
分析:
a[i]表示到i一共有多少‘a’,b[i],c[i],d[i]分别是'b','c','?'。若没有'?',则只需要在每个b出现的位置i,以该b为中心计算贡献,即ans+=a[i-1]*(c[n]-c[i]);加上'?'以后,分为四种情况。a???b??c
1.组成abc中没有问号参与,此每个?有三种可能,所以ans+=3^d[n]
2.ab?,组成的abc类型为此,所以b之后的?任取一个变成c,剩下的所有?由三种可能,所以ans+=a[i-1]*3^(d[n]-1)*(d[n]-d[i])
3.?bc,组成的abc类型为此,与2类似,所以ans+=(c[n]-c[i])*3^(d[n]-1)*d[i-1];
4.?b?,组成的abc类型为此,b之前和后面各取一个?变为a,c,剩下有3种可能,所以ans=(ans+3^(d[n]-2)*d[i-1]*(d[n]-d[i]))
#include<bits/stdc++.h>
#define N 200010
#define ll long long
using namespace std;
ll a[N],b[N],c[N],d[N],e[N];
char s[N];
int n;
const int md=1e9+7;
ll calc(){
ll ret=0;
a[0]=0;b[0]=0;c[0]=0;d[0]=0;
for(int i=1;i<=n;i++){
a[i]=a[i-1]+(s[i]=='a');
b[i]=b[i-1]+(s[i]=='b');
c[i]=c[i-1]+(s[i]=='c');
d[i]=d[i-1]+(s[i]=='?');
}
for(int i=1;i<=n;i++)
if(s[i]=='b')ret=(ret+a[i-1]*(c[n]-c[i])%md)%md;
return ret;
}
ll ans=0;
void dfs(int x){
if(x>n){
ans=(ans+calc())%md;
return;
}
if(s[x]=='?'){
s[x]='a';
dfs(x+1);
s[x]='b';
dfs(x+1);
s[x]='c';
dfs(x+1);
}else dfs(x+1);
}
int main(){
scanf("%d",&n);
scanf("%s",s+1);
e[0]=1;
for(int i=1;i<=n;i++)e[i]=e[i-1]*3%md;
ans=0;
a[0]=0;b[0]=0;c[0]=0;d[0]=0;
for(int i=1;i<=n;i++){
a[i]=a[i-1]+(s[i]=='a');
b[i]=b[i-1]+(s[i]=='b');
c[i]=c[i-1]+(s[i]=='c');
d[i]=d[i-1]+(s[i]=='?');
}
for(int i=2;i<n;i++){
if(s[i]=='b'){
ans=(ans+a[i-1]*(c[n]-c[i])%md*e[d[n]]%md)%md;
if(d[n]>=1)ans=(ans+a[i-1]*e[d[n]-1]*(d[n]-d[i])%md)%md;
if(d[n]>=1)ans=(ans+(ll)(c[n]-c[i])*e[d[n]-1]*d[i-1]%md)%md;
if(d[n]>=2)ans=(ans+(ll)e[d[n]-2]*d[i-1]%md*(d[n]-d[i])%md)%md;
// cout<<ans<<endl;
}if(s[i]=='?'){
if(d[n]>=1)ans=(ans+(ll)a[i-1]*(c[n]-c[i])%md*e[d[n]-1]%md)%md;
if(d[n]>=2)ans=(ans+(ll)a[i-1]*e[d[n]-2]*(d[n]-d[i])%md)%md;
if(d[n]>=2)ans=(ans+(ll)(c[n]-c[i])*e[d[n]-2]*d[i-1]%md)%md;
if(d[n]>=3)ans=(ans+e[d[n]-3]*d[i-1]%md*(d[n]-d[i])%md)%md;
}
}
printf("%lld\n",ans);
// ans=0;
//dfs(1);cout<<ans;
return 0;
}