#pragma comment(linker, "/STACK:1024000000,1024000000")//防止爆栈用的
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string.h>
#include <vector>
#include <queue>
#define LL unsigned __int64 //定义成这样就不需要考虑%mod(mod为2^64)
using namespace std;
int k;
//注意这里和poj2778不同,它要我们计算的是从1-n长度的单词数(要用到两次两分),而poj那道只要我们计算长度为n的单词数
//注意ac自动机fail指针和kmp中的next类似,fail指针所跳到的地方是它本身最大的后缀和跳到的地方的最大前缀
struct node //定义静态字典树
{
int isword;
int next[26];
int fail; //定义失败指针
void init()
{
for(int t=0; t<26; ++t)
{
next[t]=0;
}
fail=-1;
isword=0;
}
}a[35];
int newnode()
{
a[k].init();
return k;
}
void insert(char *p) //建树
{
int t, j, g=0, h; //g=0表示根
j=strlen(p);
for(t=0; t<j; ++t)
{
h=p[t]-'a'; //将字符转化为相应的数字
if(!a[g].next[h])
{
a[g].next[h]=newnode();
k++;
}
g=a[g].next[h];
}
a[g].isword=1; //结尾标记
return ;
}
void acAutomation() //构建失败指针fail
{
int t;
queue<int> q;
q.push(0);
while(!q.empty())
{
int s=q.front();
q.pop();
for(t=0; t<26; ++t)
{
if(a[s].next[t]==0)
{
if(s==0)
a[s].next[t]=0;
else a[s].next[t]=a[a[s].fail].next[t]; //不是根节点的话就是s节点的fail所指的节点的next[t]位置
}
else
{
if(s==0)
{
a[a[s].next[t]].fail=0;
}
else
{
int temp=a[s].fail;
while(temp!=-1)
{
if(a[temp].next[t])
{
a[a[s].next[t]].fail=a[temp].next[t];
a[a[s].next[t]].isword|=a[a[temp].next[t]].isword; //a[s].next[t]的最大后缀就是a[a[temp].fail].next[t]前缀,如果a[a[temp].fail].next[t]有标记,则a[s].next[t]也要标上
break;
}
temp=a[temp].fail;
}
if(temp==-1) //到根节点也找不到对应的next
{
a[a[s].next[t]].fail=0;
}
}
q.push(a[s].next[t]);
}
}
}
return ;
}
struct matrix
{
LL b[35][35]; //防止溢出
void init()
{
int t, j;
for(j=0; j<35; ++j)
{
for(t=0; t<35; ++t)
b[j][t]=0;
}
}
matrix operator*(matrix a1) //重载可以减少内存,但速度会慢点
{
matrix q;
q.init();
int j, t, g;
for(j=0; j<k; ++j)
{
for(t=0; t<k; ++t)
{
for(g=0; g<k; ++g)
{
q.b[j][t]+=b[j][g]*a1.b[g][t];
}
}
}
return q;
}
matrix operator+(matrix a1)
{
matrix q;
int j, t;
for(j=0; j<k; ++j)
{
for(t=0; t<k; ++t)
q.b[j][t]=b[j][t]+a1.b[j][t];
}
return q;
}
}p;
matrix f(int x) //矩阵快速幂,求出A^n
{
matrix q, s=p;
int j, t;
for(j=0; j<k; ++j)
{
for(t=0; t<k; ++t)
{
if(j==t)
q.b[j][t]=1;
else q.b[j][t]=0;
}
}
while(x)
{
if(x&1)
q=q*s;
x=x>>1;
s=s*s;
}
return q;
}
matrix f1(int x) //二分计算矩阵 A^1 + A^2 + A^3 + A^4 + A^5 + ...A^x --------构建无词根的矩阵
{
if(x==1)return p;
matrix temp=f1(x/2);
matrix s;
if(x&1)
{
s=temp+temp*f(x/2);
s=s+f(x);
}
else s=temp+temp*f(x/2);
return s;
}
LL f2(int x) //二分计算26^x
{
if(x==1)return 26;
LL s=f2(x/2);
s=s*s;
if(x&1)s*=26;
return s;
}
LL fsum(int x) //计算长度不超过x的单词总数 26^1 + 26^2 + 26^3 + 26^4 + 26^5 + ...26^x
{
if(x==1)return 26;
LL q=fsum(x/2);
LL s;
s=q+f2(x/2)*q;
if(x&1)
s+=f2(x);
return s;
}
int main()
{
int n, m, t, j, g;
char q[10];
while(scanf("%d%d", &m, &n)!=EOF)
{
a[0].init();
k=1;
for(t=0; t<m; ++t)
{
scanf("%s", q);
insert(q);
}
acAutomation();
p.init();
for(j=0; j<k; ++j) //构建无词根的矩阵
{
if(a[j].isword)continue;
for(t=0; t<26; ++t)
{
g=a[j].next[t];
if(a[g].isword)continue;
p.b[j][g]++;
}
}
matrix result=f1(n); //矩阵快速幂
LL ans, sum;
for(t=0, ans=0; t<k; ++t) //统计没有词根的数目
{
ans+=result.b[0][t];
}
sum=fsum(n); //计算全部单词的数目
ans=sum-ans;
printf("%I64u\n", ans); //注意这里输出的是%I64u
}
return 0;
}
hdu 2243 是poj2778的升级版
最新推荐文章于 2024-04-26 10:56:06 发布