题目大意
有一个ab字符串,初始为空。
用
PaPa+Pb
的概率在末尾添加字母a,有
PbPa+Pb
的概率在末尾添加字母b,当出现≥k个ab子串时立即停止添加字母,求最后期望的ab子串个数。(子串ab不要求连续)
例子:当k=1,aab含2个ab,bbabbab时不可能出现的,因为到了bbab就会停止添加字母。
题解
dp[i][j]表示前缀包含i个a,j个ab子串的所有字符串,得到的期望ab子串个数。
转移:
在此前缀后添上a:
dp[i][j]+=dp[i+1][j]×PaPa+Pb
在此前缀后添上b:
dp[i][j]+=dp[i][j+i]×PbPa+Pb
初始状态:
dp[i][j]=j (j≥k)
目标状态:
dp[0][0]
(无前缀,代表所有串)
但这两条↑是错的
还有一些无穷情况:
1.前缀出现了无穷多个b:bbbbb…….bbbbaababa…
显然在第一个a之前的所有b都没有任何用处,为了结束添加字母,则必定会出现a,所以目标状态一定会出现前缀有一个a,没有ab的情况,所以,目标状态应定义为
dp[1][0]
2.出现了无穷多个a:ababaaaa……..aaaaaab,虽然这样概率很小,但是把b添加在末尾,会增加一大堆ab,不可忽略,如果使用dp,会使i值增加到无穷大。
所以,将初始状态定义为
dp[i][j] (i+j≥k)
,然后通过数学计算:
令
pa=PaPa+Pb
,
pb=PbPa+Pb
,
S=dp[i][j]
则初始状态
代码
我用的记忆化搜索,方便理解
#include<cstdio>
#include<cstring>
const int MAXK=1005,MOD=1000000007;
int pow_mod(int a,int b)
{
int res=1;
while(b)
{
if(b&1)
res=(1LL*res*a)%MOD;
b>>=1;
a=(1LL*a*a)%MOD;
}
return res;
}
int inv(int x)
{return pow_mod(x,MOD-2);}
int dp[MAXK][MAXK];
int k,pa,pb;
int DP(int i,int j)
{
if(i+j>=k)
return (i+j+1LL*pa*inv(pb)%MOD)%MOD;
if(dp[i][j]!=-1)
return dp[i][j];
return dp[i][j]=(1LL*((1LL*pa*DP(i+1,j))%MOD+(1LL*pb*DP(i,i+j))%MOD)*inv(pa+pb))%MOD;
}
int main()
{
scanf("%d%d%d",&k,&pa,&pb);
memset(dp,-1,sizeof dp);
printf("%d\n",DP(1,0));
return 0;
}