Problem Description
背单词,始终是复习英语的重要环节。在荒废了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实在是数不出来了,现在就请你帮帮他。
一天,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实在是数不出来了,现在就请你帮帮他。
Input
本题目包含多组数据,请处理到文件结束。
每组数据占两行。
第一行有两个正整数N和L。(0<N<6,0<L<2^31)
第二行有N个词根,每个词根仅由小写字母组成,长度不超过5。两个词根中间用一个空格分隔开。
每组数据占两行。
第一行有两个正整数N和L。(0<N<6,0<L<2^31)
第二行有N个词根,每个词根仅由小写字母组成,长度不超过5。两个词根中间用一个空格分隔开。
Output
对于每组数据,请在一行里输出一共可能的单词数目。
由于结果可能非常巨大,你只需要输出单词总数模2^64的值。
由于结果可能非常巨大,你只需要输出单词总数模2^64的值。
Sample Input
2 3 aa ab 1 2 a
Sample Output
104 52
思路:用总的情况减去不可能的情况。
/*
设f(n)=26+26^1+26^2+26^3+...+26^n,则f(n+1)=26*f(n)+26,
所以|f(n+1)| = |26 26| *|f(n)|
| 1 | | 0 1| | 1 |
|f(n)| = |26 26|^(n-1) *|26|
| 1 | | 0 1| | 1|
在构造矩阵m[sz][sz]的时候,可以将矩阵变为m[sz+1][sz+1],m[][sz+1]全部为1,这样就可以计算
矩阵幂的和
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef unsigned long long ll;
const int kind=26;
const int maxn=40;
struct Matrix
{
int r,c;
ll a[maxn][maxn];
void init(int r,int c)
{
this->r=r;
this->c=c;
memset(a,0,sizeof(a));
}
void clear()
{
memset(a,0,sizeof(a));
r=c=0;
}
Matrix operator *(const Matrix &b)
{
Matrix temp;
temp.init(r,b.c);
for(int i=0;i<r;i++)
{
for(int j=0;j<b.c;j++)
{
for(int k=0;k<c;k++)
temp.a[i][j]+=(a[i][k]*b.a[k][j]);
}
}
return temp;
}
};
Matrix matrix;
Matrix pow(Matrix &a,int n)
{
Matrix ret ;
ret.init(a.r,a.c);
for(int i=0;i<a.r;i++)
ret.a[i][i] = 1;
Matrix tmp = a;
while(n)
{
if(n&1)ret=ret*tmp;
tmp=tmp*tmp;
n>>=1;
}
return ret;
}
struct Aho_Corasick
{
int ch[maxn][kind],sz;
int val[maxn],fail[maxn],last[maxn];
void init()
{
val[0]=0,sz=1;
memset(ch[0],0,sizeof(ch[0]));
}
void insert(char *s,int v)
{
int u=0,len=strlen(s);
for(int i=0;i<len;i++)
{
int id=s[i]-'a';
if(!ch[u][id])
{
memset(ch[sz],0,sizeof(ch[sz]));
val[sz]=0;
ch[u][id]=sz++;
}
u=ch[u][id];
}
val[u]=v;
}
void getFail()
{
queue<int>q;
fail[0]=0;
for(int i=0;i<kind;i++)
{
int u=ch[0][i];
if(u)
{
fail[u]=last[u]=0;
q.push(u);
}
}
while(!q.empty())
{
int r=q.front();
q.pop();
if(val[fail[r]]) val[r]=1;
for(int i=0;i<kind;i++)
{
int u=ch[r][i];
if(!u)
{
ch[r][i]=ch[fail[r]][i];
continue;
}
q.push(u);
int v=fail[r];
while(v&&!ch[v][i]) v=fail[v];
fail[u]=ch[v][i];
last[u]=val[fail[u]]?fail[u]:fail[last[u]];
}
}
}
void buildMatrix()
{
matrix.init(sz+1,sz+1);
for(int i=0;i<sz;i++)
{
for(int j=0;j<kind;j++)
{
int u=ch[i][j];
if(!val[u])
matrix.a[i][u]++;
}
}
for(int i=0;i<sz+1;i++)
matrix.a[i][sz]=1;
}
}ac;
char P[20];
int main()
{
Matrix mat;
mat.init(2,2);
mat.a[0][0]=mat.a[0][1]=26,mat.a[1][0]=0,mat.a[1][1]=1;
int N;ll L;
while(cin>>N>>L)
{
ac.init();
ll ans1=0;
Matrix res;
res.init(2,1);
res.a[0][0]=26,res.a[1][0]=1;
res=pow(mat,L-1)*res;
ans1=res.a[0][0];
matrix.clear();
for(int i=1;i<=N;i++)
{
scanf("%s",P);
ac.insert(P,i);
}
ac.getFail();
ac.buildMatrix();
Matrix cur;
ll ans2=0;
matrix=pow(matrix,L);
for(int i=0;i<matrix.r;i++)
ans2+=matrix.a[0][i];
ans2--;
ll ans=ans1-ans2;
cout<<ans<<endl;
}
return 0;
}