HDU 2243 考研路茫茫——单词情结 (AC自动机+矩阵快速幂求和)

题目大意

  • 给出n个模板,求长度小于等于m的字符串中有多少个包含这n个模板中至少一个,所有字符串均是小写字母组成,答案对2^64取模。

分析

  • 对2^64取模,用unsiged __int64 即可实现自动取模
  • 转化为 总的数量-不包含模板的数量
  • 建立AC自动机,构造矩阵M,M[i][j]表示节点i走到节点j只走一步的方法数,从而

     ans = (26 + 26^2 + ... + 26^m) - (M + M^2 + ... + M^m)
    
  • 要求M + M^2 + … + M^m,先构造一个新的矩阵A

    A =      M  M
             0  E  ,其中E为单位矩阵
    

    求A的m次幂,右上角矩阵即是 M + M^2 + … + M^m

  • 对于求26 + 26^2 + … + 26^m,同样可以采用矩阵快速幂求解
  • 然后把最终得到的两个结果相减即可,不用考虑会出现负数的情况

代码

/* 给出n个模板,
 * 求长度小于等于m的字符串中有多少个包含这n个模板中至少一个
 * 所有字符串均是小写字母组成
 * 转化为 总的数量-不包含模板的数量
 * 答案对 2^64 取模, 去unsigned __int64,即自动取模
 * 即 (26 + 26^2 + ... + 26^m) - num
 * num = A + A^2 + ... + A^m
 * 对模板建立AC自动机,然后构造矩阵A,转化为矩阵M
 * A A
 * 0 1
 * 求M的m次幂,右上角的矩阵即是A + A^2 + ... + A^m
 * 求26 + 26^2 + ... + 26^m 也可以用矩阵快速幂求
 */
#include <iostream>
#include <cstring>
#include <string>
#include <queue>

using namespace std;
#define LL unsigned __int64
const int maxn = 80;
const int sigma_size = 26;

struct Matrix {
    LL mt[maxn][maxn];
    Matrix() {memset(mt , 0 , sizeof(mt));}
    Matrix(int n) {
        memset(mt , 0 , sizeof(mt));
        for(int i = 0; i < n; i++) mt[i][i] = 1;
    }
};
struct AC {
    int ch[maxn][sigma_size];
    int val[maxn]; //val[i]为1表示节点i是不可行状态
    int sz;
    void Init() {
        sz = 1; val[0] = 0;
        memset(ch[0] , 0 , sizeof(ch[0]));
    }
    int idx(char c) {return c - 'a';}
    void Insert(string const &s)
    {
        int cur = 0 , len = s.length();
        for(int i = 0; i < len; i++) {
            int u = idx(s[i]);
            if(!ch[cur][u]) {
                memset(ch[sz] , 0 , sizeof(ch[sz]));
                val[sz] = 0;
                ch[cur][u] = sz++;
            }
            cur = ch[cur][u];
        }
        val[cur] = 1;
    }
    int f[maxn];
    void getFail()
    {
        queue<int> Q;
        for(int i = 0; i < sigma_size; i++) {
            int u = ch[0][i];
            if(u) {Q.push(u); f[u] = 0;}
            else ch[0][i] = 0;
        }
        while(!Q.empty()) {
            int cur = Q.front(); Q.pop();
            for(int i = 0; i < sigma_size; i++) {
                int u = ch[cur][i];
                if(!u) {ch[cur][i] = ch[f[cur]][i]; continue;}
                Q.push(u);
                int v = f[cur];
                while(v && !ch[v][i]) v = f[v];
                f[u] = ch[v][i];
                //若节点的失败指针指向的状态为不可行状态,则该节点也是不可行状态
                if(val[f[u]]) val[u] = 1;
            }
        }
    }
    int getMatrix(Matrix &M)
    {
        LL pt[maxn][maxn] = {{0} , {0}} , node[maxn*maxn]; int cnt = 0;
        for(int i = 0; i < sz; i++) for(int j = 0; j < sigma_size; j++) {
            int u = ch[i][j];
            if(!val[u] && !val[i]) pt[i][u]++;
        }
        for(int i = 0; i < sz; i++) if(!val[i]) node[cnt++] = i;
        //构造矩阵M
        for(int i = 0; i < cnt; i++) for(int j = 0; j < cnt; j++) {
            M.mt[i][j] = pt[node[i]][node[j]];
            M.mt[i][j+cnt] = M.mt[i][j];
        }
        for(int i = cnt; i < 2 * cnt; i++) M.mt[i][i] = 1;
        return cnt;
    }
};
//n*n的矩阵A*B
Matrix Mat_mul(Matrix const &A , Matrix const &B , int n)
{
    Matrix rel;
    for(int i = 0; i < n; i++) for(int k = 0; k < n; k++) if(A.mt[i][k]) {
        for(int j = 0; j < n; j++) rel.mt[i][j] += A.mt[i][k] * B.mt[k][j];
    }
    return rel;
}
//n*n的矩阵A^mi
Matrix Mat_pow(Matrix A , int n , int mi)
{
    if(1 == n) return A;
    Matrix rel(n);
    while(mi) {
        if(mi&1) rel = Mat_mul(rel , A , n);
        A = Mat_mul(A , A , n);
        mi >>= 1;
    }
    return rel;
}
//快速幂求26 + 26^2 + ... + 26^mi
LL get_sum(int mi)
{
    Matrix A(2);
    A.mt[0][0] = A.mt[0][1] = 26;
    A = Mat_pow(A , 2 , mi);
    return A.mt[0][1];
}
AC ac;
int main()
{
    int m , n;
    while(cin >> n >> m)
    {
        ac.Init();
        for(int i = 0; i < n; i++) {
            string s; cin >> s;
            ac.Insert(s);
        }
        ac.getFail();
        Matrix M;
        int cnt = ac.getMatrix(M);
        LL ans = get_sum(m) , num = 0;
        M = Mat_pow(M , 2 * cnt , m);
        /*
        for(int i = 0; i < 2 * cnt; i++) {
            for(int j = 0; j < 2 * cnt; j++) cout << M.mt[i][j] << "  ";
            cout << endl;
        }
        */
        for(int i = cnt; i < 2 * cnt; i++) num += M.mt[0][i];
        cout << ans - num << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值