链接:
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 = 261+262+..+26^m.
有s+1 = 1+261+..+26m.另f(n) = s+1, 则f(n) = 26*f(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;
}