[AC自动机+期望DP] 玲珑ACM 1025 - Magic boy Bi Luo with his excited string problem

Magic boy Bi Luo with his excited string problem

题意:

给出<=15个字母串,每个串不超过10个字符,然后一个人开始写字母,每次随机写一个字母,直到出现给出的任意一个串,然后就停止,问写的长度的期望,答案mod 1e9+7。

思路:

总共150个字母比较少,用ac自动机建立转移矩阵, dp[i] 表示从状态i出发写的期望,然后套高斯消元解方程。
每个状态可以写一个方程:

dp[i]=126dp[son[i][0]]+126dp[son[i][1]]++126dp[son[i][25]]+1

然后乘个26解决分数,移项过去就变成矩阵形式了。

//
//  main.cpp
//  1025
//
//  Created by 翅膀 on 16/9/16.
//  Copyright © 2016年 kg20006. All rights reserved.
//

#include <iostream>
#include <queue>
#include <stdio.h>
#include <algorithm>
#include <math.h>
#include <string.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
const int N = 155;
ll qpow(ll a, ll k) {
    ll res = 1;
    a = (a%mod+mod)%mod;
    while(k) {
        if(k&1) res = (res*a)%mod;
        a = (a*a)%mod;
        k >>= 1;
    }
    return res;
}
ll a[N][N];
ll x[N];
void swapcol(int x, int y, int row){
    for(int i = 0; i < row; ++i){
        swap(a[i][x], a[i][y]);
    }
}
void getans(int row, int col){
    for(int i = col-1; i >= 0; --i){
        ll tmp = a[i][col];
        for(int j = i+1; j < col; ++j){
            tmp = ((tmp-a[i][j]*x[j]%mod)%mod+mod)%mod;
        }
        ll X = tmp*qpow(a[i][i], mod-2)%mod;
        x[i] = X;
    }
}
int Gauss(int row, int col){
    int r = 0, c = 0;
    for(; r < row && c < col; ++r, ++c){
        int mx = r;
        for(int i = r+1; i < row; ++i){
            if(abs(a[i][c]) > abs(a[mx][c])) mx = i;
        }
        if(mx != r) swap(a[mx], a[r]);
        if(a[r][c] == 0){ r--; continue; }
        for(int i = r+1; i < row; ++i){
            if(!a[i][c]) continue;
            ll tmp1 = a[r][c], tmp2 = a[i][c];
            for(int j = c; j <= col; ++j){
                a[i][j] = (a[i][j]*tmp1 - a[r][j]*tmp2)%mod;
                if(a[i][j] < 0) a[i][j] += mod;
            }
        }
    }
    for(int i = r; i < row; ++i){
        if(a[i][col]) return -1;
    }
    int free_x = col-r; //自由元的个数
    for(int i = 0, j; i < col && i < row; ++i){
        if(!a[i][i]){
            for(j = i+1; j < col; ++j) if(a[i][j]) break;
            if(j < col) swapcol(i, j, row);
        }
    }
    getans(row, col);
    return free_x;
}
struct Trie{
    static const int MX = 155;
    int son[MX][26], fail[MX], end[MX];
    int root, alloc;
    int newnode(){
        memset(son[++alloc], 0, sizeof(son[alloc]));
        end[alloc] = 0, fail[alloc] = 0;
        return alloc;
    }
    void init(){
        alloc = 0;
        root = newnode();
    }
    void insert(char *s) {
        int p = root;
        for(int i = 0; s[i]; ++i) {
            if(son[p][s[i]-'a'] == 0) son[p][s[i]-'a'] = newnode();
            p = son[p][s[i]-'a'];
        }
        end[p] = 1;
    }
    void build() {
        queue<int>q;
        for(int i = 0; i < 26; ++i) {
            if(son[root][i] == 0) son[root][i] = root;
            else fail[son[root][i]] = root, q.push(son[root][i]);
        }
        while(!q.empty()){
            int u = q.front(); q.pop();
            if(end[fail[u]]) end[u] = 1;
            for(int i = 0; i < 26; ++i) {
                if(son[u][i] == 0) son[u][i] = son[fail[u]][i];
                else fail[son[u][i]] = son[fail[u]][i], q.push(son[u][i]);
            }
        }
    }
    ll DP(){
        memset(a, 0, sizeof(a));
        memset(x, 0, sizeof(x));
        for(int i = 1; i <= alloc; ++i){
            if(end[i]){
                a[i-1][i-1] = 1;
                a[i-1][alloc] = 0;
                continue;
            }
            a[i-1][i-1] = 26;
            for(int j = 0; j < 26; ++j) {
                a[i-1][son[i][j]-1] -= 1;
            }
            a[i-1][alloc] = 26;
        }
        Gauss(alloc, alloc);
        return x[0];
    }
}ac;
char s[25];
int main(int argc, const char * argv[]) {
    int T, ca = 1;
    scanf("%d", &T);
    while(T--) {
        ac.init();
        int n;
        scanf("%d", &n);
        for(int i = 0; i < n; ++i) {
            scanf("%s", s);
            ac.insert(s);
        }
        ac.build();
        printf("Case #%d: %lld\n", ca++, ac.DP());
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值