UVALive 6495 Probability Paradox AC自动机+高斯消元

题目大意:两个人又两个等长但不同的字符串,现在在一张纸上随机的写字母T和字母H,指导纸上写的字符串包含了某个人的串,如果包含了第一个人的串,那么第一个就获胜,如果是第二个人的串,则第二个人获胜。

好久没做AC自动机了,感觉都生疏了。这题其实不是很难的,关键是第一眼可能被分式吓到了。我们不妨用两人的串构造AC自动机,然后用dp[i]表示当前状态为AC自动机上的i,第一个人获胜的概率。

如样例1“TT HH”构造的AC自动机可以是下图,节点上的数字表示节点标号,我们就用这个标号表示AC自动机上的状态。


我们很容易得到dp方程

  • dp[0] = 0.5*dp[1] + 0.5*dp[3]
  • dp[1] = 0.5*dp[2] + 0.5*dp[3]
  • dp[2] = 1
  • dp[3] = 0.5*dp[1] + 0.5*dp[4]
  • dp[4] = 0
我们可以解得dp[0] = 0.5。

其实这个还有个需要注意的细节问题:是否有非标记节点(即根到这个点的路径,表示的是最开始的那两个串)能通过fail指针到达模式串?但是由于两串相同长度,不会有这种情况。

因为直接转移似乎很麻烦,我们采用gauss消元,而这个因为是分式,则计算的时候不是用double,而是用两个long long(结构体)表示一个分数。

//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef long long LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const LL INF=1LL<<60;
const double INFF=1e100;
const double eps=1e-8;
const int mod=1000000007;
const int NN=100005;
const int MM=1000010;
/* ****************** */

char ss[15];
const int k_size=2;
const int LEN=12;
struct Tire_Tree
{
    int fail;
    int p;//0,1,-1
    int next[k_size];
    void init()
    {
        p=-1;
        fail=-1;
        memset(next,-1,sizeof(next));
    }
}tire[2*LEN];
int tire_cnt;
int tire_q[2*LEN];
int dui[2*LEN];

LL gcd(LL a,LL b)
{
    if(b==0)return a;
    return gcd(b,a%b);
}
struct Fraction
{
    LL a,b;

    Fraction(LL x=1,LL y=1):a(x),b(y){};
    Fraction(const Fraction &tt):a(tt.a),b(tt.b){};

    void init()
    {
        LL g=gcd(a,b);
        a/=g;
        b/=g;
        if(a==0)b=1;
    }

    double val()
    {
        return (a+0.0)/b;
    }

    bool operator<(const Fraction &tt)const
    {
        return a*tt.b < tt.a*b;
    }
    bool operator>(const Fraction &tt)const
    {
        return a*tt.b > tt.a*b;
    }
    bool operator==(const Fraction &tt)const
    {
        return a*tt.b == tt.a*b;
    }
    bool operator!=(const Fraction &tt)const
    {
        return (a*tt.b) != (tt.a*b);
    }
    bool operator<=(const Fraction &tt)const
    {
        return a*tt.b <= tt.a*b;
    }
    bool operator>=(const Fraction &tt)const
    {
        return a*tt.b >= tt.a*b;
    }

    Fraction operator+(const Fraction &tt)const
    {
        Fraction temp;
        temp.a = a*tt.b + tt.a*b;
        temp.b = b*tt.b;
        temp.init();
        return temp;
    }
    Fraction operator-(const Fraction &tt)const
    {
        Fraction temp;
        temp.a = a*tt.b - tt.a*b;
        temp.b = b*tt.b;
        temp.init();
        return temp;
    }
    Fraction operator*(const Fraction &tt)const
    {
        Fraction temp;
        temp.a = a*tt.a;
        temp.b = b*tt.b;
        temp.init();
        return temp;
    }
    //如果是除以0,这这个分式变为 ?/0
    Fraction operator/(const Fraction &tt)const
    {
        Fraction temp;
        temp.a = a*tt.b;
        temp.b = b*tt.a;
        temp.init();
        return temp;
    }
};

void tire_insert(char *str,int le,int root)
{
    int i,x,p=root;
    for(i=0;str[i];i++)
    {
        if(str[i]=='T')
            x=0;
        else
            x=1;
        if(tire[p].next[x]==-1)
        {
            tire_cnt++;
            tire[tire_cnt].init();
            tire[p].next[x]=tire_cnt;
        }
        p=tire[p].next[x];
    }
    tire[p].p=le;
}
void init_fail(int root)
{
    int head,tail,i,fa,p=root;

    head=1;
    tail=0;
    tire[root].fail=root;
    for(i=0;i<k_size;i++)
    {
        if(tire[p].next[i]!=-1)
        {
            tire_q[++tail]=tire[p].next[i];
            tire[ tire_q[tail] ].fail=root;
        }
        else
            tire[p].next[i]=root;
    }

    while(head<=tail)
    {
        p=tire_q[head++];
        fa=tire[p].fail;

        for(i=0;i<k_size;i++)
        {
            if(tire[p].next[i]!=-1)
            {
                tire_q[++tail]=tire[p].next[i];
                tire[ tire_q[tail] ].fail=tire[fa].next[i];
            }
            else
                tire[p].next[i]=tire[fa].next[i];
        }
    }
}


Fraction gss[2*LEN][2*LEN+1];
Fraction gss_ans[2*LEN];
//行列编号都从0开始,包含系数列,矩阵大小为n*m
//此函数保证只有1个解才能用
void gauss(int n,int m)
{
    int i,j,k,pca;
    Fraction temp,temp1;
    for(i=0;i<n;i++)
    {
        pca=i;
        for(j=i+1;j<n;j++)
        {
            if( fabs( gss[j][i].val() ) > fabs( gss[pca][i].val() ))
                pca=j;
        }
        if(pca!=i)
        {
            for(j=i;j<m;j++)
            {
                swap(gss[i][j],gss[pca][j]);
            }
        }
        //如果此位已经是0,则答案是无限个解或无解?应该是吧
        if( gss[i][i] != Fraction(0,1) )
        {
            for(j=i+1;j<n;j++)
            {
                temp = gss[j][i]/gss[i][i];
                gss[j][i] = Fraction(0,1);
                for(k=i+1;k<m;k++)
                {
                    temp1 = gss[i][k]*temp;
                    gss[j][k] = gss[j][k] - temp1;
                }
            }
        }
    }
    //因为是唯一解,所以这么写
    for(i=m-2;i>=0;i--)
    {
        gss_ans[i]=gss[i][m-1];
        for(j=i+1;j<m-1;j++)
        {
            temp = gss_ans[j]*gss[i][j];
            gss_ans[i] = gss_ans[i] - temp;
        }
        gss_ans[i] = gss_ans[i]/gss[i][i];
    }
}

int main()
{
    int i,j,tire_root;
    int n,m,tol;
    int y,z;
    while(1)
    {
        scanf("%s",ss);
        if(strcmp(ss,"$")==0)break;

        tire_root=tire_cnt=0;
        tire[0].init();
        tire_insert(ss,1,tire_root);
        scanf("%s",ss);
        tire_insert(ss,0,tire_root);
        init_fail(tire_root);

        n=tire_cnt+1;
        m=tire_cnt;
        tol=0;
        for(i=0;i<=tire_cnt;i++)
        {
            if(tire[i].p==-1)
            {
                dui[i]=tol++;
            }
        }
        for(i=0;i<n;i++)
            for(j=0;j<m;j++)
            {
                gss[i][j] = Fraction(0,1);
            }
        for(i=0;i<=tire_cnt;i++)
        {
            if(tire[i].p!=-1)continue;

            y=tire[i].next[0];
            z=tire[i].next[1];

            gss[i][ dui[i] ] = gss[i][ dui[i] ]+Fraction(1,1);

            if(tire[y].p==-1)
            {
                gss[i][ dui[y] ] = gss[i][ dui[y] ]+Fraction(-1,2);
            }
            else if(tire[y].p==1)
            {
                gss[i][m-1] = gss[i][m-1] + Fraction(1,2);
            }

            if(tire[z].p==-1)
            {
                gss[i][ dui[z] ] = gss[i][ dui[z] ]+Fraction(-1,2);
            }
            else if(tire[z].p==1)
            {
                gss[i][m-1] = gss[i][m-1] + Fraction(1,2);
            }
        }

        gauss(n,m);

        printf("%lld/%lld\n",gss_ans[dui[0]].a,gss_ans[dui[0]].b);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值