题目链接:https://vjudge.net/problem/UVALive-3490
题意:随机字母组成一个串,有一个目标串,当这个由随机字母组成的串出现目标串就停止,求这个随机字母
组成串的期望长度。
解法:容易看出,dp[i] = 所有他下一步可能的节点dp[j]之和/m+1。可以想一下从头走到i的递推式,是dp[i]
= 所有走到i这个节点的dp[j]+1。
然后就可以列出n个方程,dp[最后一个节点] = 0。然后用高斯消元求解。double会有误差,所以用了整数
的,做法很简单,把方程式子左右乘个m,然后消元的时候比如第一行是3,第二行那个位置系数是2,那么对
于后面的系数x1(第一行),x2(第二行),x2*3-x1*2即可。
///UVALVE 3490
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 20;
struct Matrix{
LL m[20][20];
}x;
struct AhoCorasick{
int sz, ch[maxn][26], val[maxn], f[maxn];
void clear(){
memset(ch[0], 0, sizeof(ch[0]));
sz=1;
}
int idx(char c){return c-'A';}
void insert(char *s){
int u=0, n=strlen(s);
for(int i=0; i<n; i++){
int c=idx(s[i]);
if(!ch[u][c]){
memset(ch[sz], 0, sizeof(ch[sz]));
val[sz]=0;
ch[u][c]=sz++;
}
u=ch[u][c];
}
val[u]=1;
}
void getfail(){
queue<int>q;
f[0]=0;
for(int c=0;c<26;c++){
int u=ch[0][c];
if(u){
f[u]=0;
q.push(u);
}
}
while(!q.empty()){
int r=q.front();q.pop();
for(int c=0; c<26; c++){
int u=ch[r][c];
if(!u){
ch[r][c]=ch[f[r]][c];
continue;
}
q.push(u);
int v=f[r];
while(v&&!ch[v][c]) v=f[v];
f[u]=ch[v][c];
}
}
}
void Getmaze(int num, int n){
memset(x.m, 0, sizeof(x.m));
for(int i=0; i<n-1; i++){
x.m[i][n]-=num;
x.m[i][i]-=num;
for(int j=0; j<num; j++) x.m[i][ch[i][j]]++;
}
x.m[n-1][n-1]=1;
x.m[n-1][n]=0;
}
}AC;
void Guass(int n){
int i, j, k, r;
for(i=0; i<n; i++){
r=i;
while(r<n && !x.m[r][i]) r++;
if(r!=i) for(j=0; j<=n; j++) swap(x.m[r][j], x.m[i][j]);
for(k=i+1; k<n; k++){
LL f=x.m[k][i]/x.m[i][i];
for(j=i;j<=n;j++) x.m[k][j]-=f*x.m[i][j];
}
}
for(i=n-1; i>=0; i--){
for(j=i+1; j<n; j++){
x.m[i][n]-=x.m[j][n]*x.m[i][j];
}
x.m[i][n]/=x.m[i][i];
}
}
int main(){
int n,t,ks=0;
char s[15];
scanf("%d", &t);
while(t--){
printf("Case %d:\n", ++ks);
scanf("%d%s", &n,s);
int len=strlen(s)+1;
AC.clear();
AC.insert(s);
AC.getfail();
//puts("lsx");
AC.Getmaze(n,len);
Guass(len);
printf("%lld\n", x.m[0][len]);
if(t!=0) printf("\n");
}
return 0;
}