题目链接:点击打开链接
题目描述:给定一些词根,求至少含有一个词根的长度<=L的字符串有多少种?
解题思路:AC自动机+矩阵快速幂
分析:这道题目和poj2778 DNA Sequence 是一样的,如果还没做过那道题目建议先去做一下再做这道
1、首先要知道图的邻接矩阵幂的含义是什么?不知道的请看:点击打开链接
有了上述概念之后:
AC自动机本身就是一张图,AC自动机上的每个状态表示图中的一个顶点,每条经过a、b、c···的状态转移相当于图中的一条边,所以求长度为n的字符串就相当于求在图中从出发点经过n步所能到达任意顶点的方案总数,而本题所要求的是<=L的,我们只需要求邻接矩阵A的A^1+A^2+···+A^n的方案数即可
关于如何求包含词根的字符串,根据poj2778我们会求解不包含词根的字符串,只要用总的减去不包含的就可以了即:(26^1+26^2+···+26^n)-(A^1+A^2+···+A^n)
对于如何求解一个数的前n次幂的和与一个矩阵的前n次幂的和,我们可以使用矩阵快速幂
譬如:26^1+26^2+···+26^n
|1 26| |0|
|0 26| |1|
A^1+A^2+···+A^n
|E A| |0|
|0 A| |E|
代码如下:
#include <cstdio>
#include <cstring>
#include <queue>
typedef unsigned long long ll;
using namespace std;
struct Matrix{
ll m[60][60];
int L;
Matrix(int len){
L=len;
for(int i=0;i<L;++i)
for(int j=0;j<L;++j)
m[i][j]=0;
}
Matrix operator*(const Matrix& b){
Matrix t(L);
for(int i=0;i<L;++i)
for(int j=0;j<L;++j)
for(int k=0;k<L;++k)
t.m[i][j]+=(m[i][k]*b.m[k][j]);
return t;
}
};
Matrix doexpmat(Matrix a,int num){
if(num==1) return a;
Matrix x(a.L*2);
for(int i=0;i<a.L;++i) for(int j=0;j<a.L;++j) if(i==j)x.m[i][j]=1;
for(int i=a.L;i<x.L;++i) for(int j=0;j<a.L;++j) x.m[i][j]=0;
for(int i=0;i<a.L;++i) for(int j=a.L;j<x.L;++j) x.m[i][j]=a.m[i][j-a.L];
for(int i=a.L;i<x.L;++i) for(int j=a.L;j<x.L;++j) x.m[i][j]=a.m[i-a.L][j-a.L];
Matrix t(a.L*2);
for(int i=0;i<t.L;++i) t.m[i][i]=1;
num--;
while(num){
if(num&1) t=t*x;
num=num>>1;
x=x*x;
}
Matrix tt(a.L*2);
for(int i=0;i<a.L;++i) for(int j=0;j<a.L;++j) tt.m[i][j]=a.m[i][j];
for(int i=a.L;i<t.L;++i) for( int j=0;j<a.L;++j) tt.m[i][j]=a.m[i-a.L][j];
t=t*tt;
return t;
}
ll pows(ll a,int num){
if(num==1) return a;
Matrix x(2);
x.m[0][0]=1; x.m[0][1]=a;
x.m[1][0]=0; x.m[1][1]=a;
Matrix t(2);
t.m[0][0]=1; t.m[1][1]=1;
num--;
while(num){
if(num&1) t=t*x;
num=num>>1;
x=x*x;
}
return (t.m[0][0]+t.m[0][1])*a;
}
struct Trie{
int next1[30][26],fail[30];
bool end1[30];
int root,L;
int newnode(){
for(int i=0;i<26;++i) next1[L][i]=-1;
end1[L++]=false;
return L-1;
}
void init(){
L=0;
root=newnode();
}
void insertnode(char* str){
int len=strlen(str),now=root;
for(int i=0;i<len;++i){
if(next1[now][str[i]-'a']==-1)
next1[now][str[i]-'a']=newnode();
now=next1[now][str[i]-'a'];
}
end1[now]=true;
}
void build(){
fail[root]=root;
queue<int> q;
for(int i=0;i<26;++i){
if(next1[root][i]==-1)
next1[root][i]=root;
else{
fail[next1[root][i]]=root;
q.push(next1[root][i]);
}
}
while(!q.empty()){
int now=q.front();q.pop();
if(end1[fail[now]]) end1[now]=true;
for(int i=0;i<26;++i){
if(next1[now][i]==-1)
next1[now][i]=next1[fail[now]][i];
else{
fail[next1[now][i]]=next1[fail[now]][i];
q.push(next1[now][i]);
}
}
}
}
Matrix getMatrix(){
Matrix t(L);
for(int i=0;i<L;++i) for(int j=0;j<26;++j)
if(!end1[next1[i][j]]) t.m[i][next1[i][j]]++;
return t;
}
};
int n,l;
char st[10];
Trie ac;
int main(){
while(scanf("%d%d",&n,&l)!=EOF){
ac.init();
for(int i=0;i<n;++i){ scanf("%s",st); ac.insertnode(st); };
ac.build();
Matrix t = ac.getMatrix();
t=doexpmat(t,l);
ll ans=0;
for(int i=0;i<ac.L;++i)
ans+=t.m[0][i];
ll tmp=pows(26,l);
printf("%I64u\n",tmp-ans);
}
return 0;
}