题目网址:http://acm.hdu.edu.cn/showproblem.php?pid=2243 这题用的是AC自动机加矩阵乘法去做的。首先根据给的词根建立字典树,然后建立AC自动机,利用fail指针去构造矩阵。矩阵的意思是不包含词根的状态,而把矩阵0行0到sizes-1列的值相加,就是单词长度为一时,不包含词根的情况有多少种。用最大数去减,就是单词长度为一时,包含词根的种数。同样的,矩阵相乘后,乘了N次,用最大数去减,便是长度为N的单词长度包含词根的情况,把1到N的种类相加便是答案。注意,在这里的代码中,我直接把矩阵N阶的和表示了出来,所以直接减就是答案。这里求矩阵N阶的和用的是二分法,不然会超时,假设N是一阶矩阵,N^4 = N^1 + N^2 + N^2 * (N^1 + N^2).二分法中为奇数时特殊处理。
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <queue>
#define maxc 35
#define maxn 26
#define mem(a) memset(a, 0, sizeof(a))
using namespace std;
int tree[maxc][maxn], fails[maxc], words[6], sizes;
unsigned __int64 maps[maxc][maxc], ress[maxc][maxc], tmps[maxc][maxc], tmpss[maxc][maxc], danwei[maxc][maxc], ans, rs, rss;
bool num[maxc];
char ch[6];
void inits()
{
sizes = 1;
num[0] = 0;
mem(tree[0]);
mem(maps);
mem(ress);
mem(tmps);
return;
}
void build()
{
int i, a, b = 0;
for(i = 1;i <= words[0];i++)
{
a = words[i];
if(!tree[b][a])
{
mem(tree[sizes]);
num[sizes] = 0;
tree[b][a] = sizes++;
}
b = tree[b][a];
}
num[b] = 1;
return;
}
void ac_machine_build()
{
int i, a, b;
queue<int> q;
fails[0] = 0;
for(i = 0;i < maxn;i++)
{
a = tree[0][i];
if(a)
{
fails[a] = 0;
q.push(a);
}
}
while(!q.empty())
{
b = q.front();
q.pop();
if(num[fails[b]])
num[b] = 1;
for(i = 0;i < maxn;i++)
{
a = tree[b][i];
if(!a)
{
tree[b][i] = tree[fails[b]][i];
continue;
}
q.push(a);
fails[a] = tree[fails[b]][i];
}
}
return;
}
void zh(char *word)
{
int i, len;
len = strlen(word);
words[0] = len;
for(i = 0;i < len;i++)
words[i + 1] = ch[i] - 'a';
return;
}
void mps()
{
int i, j;
mem(maps);
for(i = 0;i < sizes;i++)
{
for(j = 0;j < maxn;j++)
{
if(!num[i]&&!num[tree[i][j]])
maps[i][tree[i][j]]++;
}
}
return;
}
void mp_add(unsigned __int64 a[][maxc], unsigned __int64 b[][maxc])
{
int i, j;
for(i = 0;i < sizes;i++)
{
for(j = 0;j < sizes;j++)
{
a[i][j] = a[i][j] + b[i][j];
}
}
return;
}
void mp_sub(unsigned __int64 a[][maxc], unsigned __int64 b[][maxc])
{
int i, j;
for(i = 0;i < sizes;i++)
{
for(j = 0;j < sizes;j++)
{
a[i][j] = a[i][j] - b[i][j];
}
}
return;
}
void mp_mt(unsigned __int64 a[][maxc], unsigned __int64 b[][maxc], unsigned __int64 c[][maxc])
{
int i, j, k;
for(i = 0;i < sizes;i++)
{
for(j = 0;j < sizes;j++)
{
c[i][j] = 0;
for(k = 0;k < sizes;k++)
{
c[i][j] += (a[i][k] * b[k][j]);
}
}
}
return;
}
void mp_fz(unsigned __int64 a[][maxc], unsigned __int64 b[][maxc])
{
int i, j;
for(i = 0;i < sizes;i++)
{
for(j = 0;j < sizes;j++)
{
a[i][j] = b[i][j];
}
}
return;
}
void mp_swap(unsigned __int64 a[][maxc], unsigned __int64 b[][maxc])
{
int i, j;
unsigned long long c[maxc][maxc];
for(i = 0;i < sizes;i++)
{
for(j = 0;j < sizes;j++)
{
c[i][j] = a[i][j];
}
}
for(i = 0;i < sizes;i++)
{
for(j = 0;j < sizes;j++)
{
a[i][j] = b[i][j];
}
}
for(i = 0;i < sizes;i++)
{
for(j = 0;j < sizes;j++)
{
b[i][j] = c[i][j];
}
}
return;
}
void res(unsigned __int64 n)
{
if(n == 1)
{
int i, j;
for(i = 0;i < sizes;i++)
{
for(j = 0;j < sizes;j++)
{
tmpss[i][j] = ress[i][j] = maps[i][j];
}
}
rs = rss = 26;
return;
}
res(n / 2);
rs = rs * rss + rs;
rss = rss * rss;
unsigned __int64 tp[maxc][maxc];
mp_mt(ress, tmpss, tp);
mp_add(ress, tp);
mp_mt(tmpss, tmpss, tmps);
if(n % 2)
{
rs = (rs + 1) * 26;
rss *= 26;
mp_add(ress, danwei);
mp_mt(ress, maps, tp);
mp_fz(ress, tp);
mp_mt(tmps, maps, tmpss);
}
else
{
mp_swap(tmps, tmpss);
}
}
int main()
{
int i, n;
unsigned __int64 m, tmp;
for(i = 0;i < 35;i++)
{
danwei[i][i] = 1;
}
while(scanf("%d%I64u", &n, &m) != EOF)
{
inits();
ans = 0;
for(i = 0;i < n;i++)
{
mem(ch);
scanf("%s", ch);
zh(ch);
build();
}
ac_machine_build();
mps();
res(m);
for(i = 0;i < sizes;i++)
ans += ress[0][i];
printf("%I64u\n", rs - ans);
}
return 0;
}