P6125 [JSOI2009]有趣的游戏

【题意】

给定n个长度为l的字符串,字符集大小为m,每次在末尾随机生成一个字符,当出现字符串的时候停止,求这n个字符串作为终止的概率

【分析】

我们能想到在串末尾位置为增加字符就很想AC自动机的转移方式,所以我们可以考虑建立出AC自动机

然后考虑问题就被转换为到AC自动机上某些点的概率

但是这样我们也很难去直接计算

因为一个点是一个串的结尾的话,就意味着游戏要结束了,所以我们的转移也会停止

到达某个点的概率就可以被到达次数的期望来代替

这时候我们就可以dp了,考虑每个非终止位置都有1/m的概率转移到儿子,列出方程高斯消元即可

注意根的位置要特判

$f_x=[x=0]+\sum_{from}f_{from}$

时间复杂度 $O(n^3l^3)$

【代码】

#include<bits/stdc++.h>
using namespace std;
typedef pair <int,double> PID;
#define mp make_pair
int n,l,m;
double p[11];
char s[11];
const int maxn=105+5;
struct ac
{
    int ch[27],tag;
}tr[maxn];
int cnt,pos[maxn];
vector <PID> G[maxn];
void insert(int x)
{
    int now=0;
    for(int i=1;i<=l;i++)
    {
        int c=s[i]-'A';
        if(!tr[now].ch[c]) tr[now].ch[c]=++cnt;
        now=tr[now].ch[c];
    }
    tr[now].tag=1;
    pos[x]=now;
}
double A[maxn][maxn],res[maxn];
void guass(int n)
{
    for(int i=1;i<=n;i++)
    {
        int pos=i;
        for(int j=i+1;j<=n;j++) if(fabs(A[j][i])>fabs(A[pos][i])) pos=j;
        for(int j=1;j<=n+1;j++)
            swap(A[i][j],A[pos][j]);
        for(int j=1;j<=n;j++)
            if(i!=j)
            {
                double t=A[j][i]/A[i][i];
                for(int k=i+1;k<=n+1;k++)
                    A[j][k]-=A[i][k]*t;
            }
    }
    for(int i=1;i<=n;i++) res[i]=A[i][n+1]/A[i][i];
}
int fail[maxn];
void getfail()
{
    queue <int> q;
    for(int i=0;i<m;i++)
        if(tr[0].ch[i])
            q.push(tr[0].ch[i]);
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=0;i<m;i++)
        {
            if(!tr[u].ch[i]) tr[u].ch[i]=tr[fail[u]].ch[i];
            else
            {
                int to=fail[u];
                fail[tr[u].ch[i]]=tr[to].ch[i];
                q.push(tr[u].ch[i]);
            }
        }
    }
}
int main()
{
//     freopen("game.in","r",stdin);
    scanf("%d%d%d",&n,&l,&m);
    int x,y;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        p[i]=1.0*x/y;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        insert(i);
    }
    getfail();
    for(int i=0;i<=cnt;i++)
        if(!tr[i].tag)
            for(int j=0;j<m;j++)            
                    G[tr[i].ch[j]].push_back(mp(i,p[j+1]));
    for(int i=0;i<=cnt;i++)
    {
        A[i+1][i+1]=-1.0;
        if(!i) A[i+1][cnt+2]=-1.0;
        for(int k=0;k<G[i].size();k++)
            A[i+1][G[i][k].first+1]+=G[i][k].second;
    }
    guass(cnt+1);
    // for(int i=0;i<=cnt+2;i++)
    //     printf("%d ",G[i].size());
    for(int i=1;i<=n;i++) printf("%.2f\n",max(0.0,res[1+pos[i]]));
    return 0;
}

 

上一篇:快乐的一天从AC开始 | 20210705 | P3106


下一篇:学习笔记-无线的简单配置