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

链接:

https://vjudge.net/problem/HDU-2243

题意:

背单词,始终是复习英语的重要环节。在荒废了3年大学生涯后,Lele也终于要开始背单词了。
一天,Lele在某本单词书上看到了一个根据词根来背单词的方法。比如"ab",放在单词前一般表示"相反,变坏,离去"等。

于是Lele想,如果背了N个词根,那这些词根到底会不会在单词里出现呢。更确切的描述是:长度不超过L,只由小写字母组成的,至少包含一个词根的单词,一共可能有多少个呢?这里就不考虑单词是否有实际意义。

比如一共有2个词根 aa 和 ab ,则可能存在104个长度不超过3的单词,分别为
(2个) aa,ab,
(26个)aaa,aab,aac...aaz,
(26个)aba,abb,abc...abz,
(25个)baa,caa,daa...zaa,
(25个)bab,cab,dab...zab。

这个只是很小的情况。而对于其他复杂点的情况,Lele实在是数不出来了,现在就请你帮帮他。

思路:

这两天的AC自动机刷新了认知, 矩阵还能这么用.
考虑每次可以走任意方向, 在考虑不能走出现模板串的总次数, 就是所有次数-不走次数, 等于最少走一次的次数.
对于不走的次数, 就和poj-2778一样, 而所有次数, 考虑s = 26^1+26^2+..+26^m.
有s+1 = 1+26^1+..+26^m.另f(n) = s+1, 则f(n) = 26f(n-1)+1. 得出矩阵
f(n) = | 26 1 |
|f(n-1)|
1 | 0 1 | * | 1 |
两次矩阵快速幂.

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
//#include <memory.h>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#include <math.h>
#include <stack>
#include <string>
#include <assert.h>
#include <iomanip>
#include <iostream>
#include <sstream>
#define MINF 0x3f3f3f3f
using namespace std;
typedef unsigned long long LL;
const LL MAXN = 2e6+10;

struct AcTree
{
    int Next[26];
    int fail;
    int end;
    int ok;
    void Init()
    {
        memset(Next, 0, sizeof(Next));
        end = 0;
        fail = 0;
        ok = 1;
    }
}tree[MAXN];
struct Martix
{
    LL mar[110][110];
    int n;
    void Inin(const int *key, int len, const int *value)
    {
        memset(mar, 0, sizeof(mar));
        n = len;
        for (int i = 1;i <= n;i++)
        {
            for (int j = 0;j < 26;j++)
            {
                int node = tree[key[i]].Next[j];
                if (tree[node].ok == 0)
                    continue;
                mar[i][value[node]]++;
//                cout << key[i] << ' ' << node << endl;
            }
        }
        n++;
        for (int i = 1;i <= n;i++)
            mar[i][n] = 1;
    }
    Martix& operator * (const Martix& that)
    {
        LL tmp[110][110];
        for (int i = 1;i <= n;i++)
        {
            for (int j = 1;j <= n;j++)
            {
                LL tmpval = 0;
                for (int k = 1;k <= n;k++)
                    tmpval = tmpval+this->mar[i][k]*that.mar[k][j];
                tmp[i][j] = tmpval;
            }
        }
        for (int i = 1;i <= n;i++)
        {
            for (int j = 1;j <= n;j++)
                this->mar[i][j] = tmp[i][j];
        }
        return *this;
    }
}Mar;
char s[MAXN];
int Key[110], Value[110*110];
LL n, m, cnt;

void Insert(char *s)
{
    int len = strlen(s);
    int pos = 0;
    for (int i = 0;i < len;i++)

    {
        if (tree[pos].Next[s[i]-'a'] == 0)
        {
            tree[pos].Next[s[i]-'a'] = ++cnt;
            tree[cnt].Init();
        }
        pos = tree[pos].Next[s[i]-'a'];
    }
    tree[pos].end = 1;
    tree[pos].ok = 0;
}

void BuildAC()
{
    queue<int> que;
    for (int i = 0;i < 26;i++)
    {
        if (tree[0].Next[i] != 0)
            tree[tree[0].Next[i]].fail = 0, que.push(tree[0].Next[i]);

    }
    while (!que.empty())
    {
        int u = que.front();
        que.pop();
        if (tree[tree[u].fail].ok == 0)
            tree[u].ok = 0;
        for (int i = 0;i < 26;i++)
        {
            if (tree[u].Next[i] != 0)
            {
                tree[tree[u].Next[i]].fail = tree[tree[u].fail].Next[i];
                que.push(tree[u].Next[i]);
            }
            else
                tree[u].Next[i] = tree[tree[u].fail].Next[i];
        }
    }
}

Martix QuickMartixMi(Martix a, LL b)
{
    b--;
    Martix res = a;
    while (b)
    {
        if (b&1)
            res*a;
        a*a;
        b >>= 1;
    }
    return res;
}

int main()
{
    while(~scanf("%d%d", &m, &n))
    {
        cnt = 0;
        tree[0].Init();
        for (int i = 1; i <= m; i++)
        {
            scanf("%s", s);
            Insert(s);
        }
        BuildAC();
        int tmp = 0;
        for (int i = 0;i <= cnt;i++)
        {
            if (tree[i].ok)
                Key[++tmp] = i, Value[i] = tmp;
        }
        Mar.Inin(Key, tmp, Value);
        Martix res = QuickMartixMi(Mar, n);
        LL ans = 0;
        for (int i = 1;i <= res.n;i++)
            ans += res.mar[1][i];
        ans--;//不存在
        Martix Sum;
        Sum.n = 2;
        memset(Sum.mar, 0, sizeof(Sum.mar));
        Sum.mar[1][1] = 26;
        Sum.mar[1][2] = Sum.mar[2][2] = 1;
        Sum = QuickMartixMi(Sum, n);
        cout << Sum.mar[1][1]+Sum.mar[1][2]-1-ans << endl;
    }

    return 0;
}
相关文章