感觉我字符串和期望都不好……
Tag
KMP,期望
题意
有 $n$ 种字符,给定一个模式串
$S$ ,一开始字符串为空,现在每次随机生成一个 1~n 的字符添加到字符串末尾,直到出现 $S$ 停止,问长度期望。
思路
首先对S预处理,求出失配数组 fail。
dp[i] 表示末尾匹配了 i 个S串的字符所需要的次数期望。
每次枚举可能出现的字符1~n。对于S字符串,i+1肯定是确定的字符,所以对于其他字符肯定是不匹配的。
假设现在生成了 k 字符,并且 k 字符不等于 S[i+1],那么根据 S 的 fail,假设有匹配 j 个字符,也就是说从匹配 j 个到匹配 i 个我们还要重新生成 dp[i] - dp[j] 次(期望)。
(从匹配i-1到匹配i个需要生成次数的期望)$f(i)=1+\sum_{i=1}^n(dp[i-1]-dp[lose(k)])/n+\dfrac{n-1}{n}f(i)$
(lose(k)为生成字符k的情况下匹配的字符数)
$ dp[i] = dp[i-1] + f(i)$ 。
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=21;
int fail[N],n;
ll f[N];
char s[N];
void get_fail( char *s )
{
int p=0,len=strlen(s+1);
for ( int i=2; i<=len; i++ )
{
while ( p && s[p+1]!=s[i] ) p=fail[p];
if ( s[p+1]==s[i] ) p++;
fail[i]=p;
}
}
int main()
{
int T; scanf( "%d",&T );
for ( int cas=1; cas<=T; cas++ )
{
printf( "Case %d:\n",cas );
scanf( "%d%s",&n,s+1 );
get_fail(s); f[0]=0; int len=strlen(s+1);
for ( int i=1; i<=len; i++ )
{
f[i]=f[i-1]+n;
for ( int j=0; j<n; j++ )
{
if ( s[i]=='A'+j ) continue;
int p=i-1;
while ( p && s[p+1]!=j+'A' ) p=fail[p];
if ( s[p+1]==j+'A' ) p++;
f[i]+=f[i-1]-f[p];
}
}
printf( "%lld\n",f[len] );
if ( cas<T ) printf( "\n" );
}
}