HDOJ 2243 考研路茫茫——单词情结(自动机DP+矩阵快速幂和)
题意:给出N个词根,求长不超过L的且至少包含一个上述词根的单词的个数。
数据范围:0<N<6,0<L<2^31
这题与上一题差不多,但比上题要繁琐的多,关键的区别在于"不超过L" 和"至少包含一个"
因为"至少包含一个",所以刚好是求上一题的反面;
因为"不超过L",所以要求的是矩阵幂和S = A + A2 + A3 + … + Ak
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> #include <string.h> #include <queue> #include <math.h> using namespace std; #define N 6 #define LEN 6 #define SIZE (N*LEN) typedef unsigned __int64 LL; int n,l,node; int next[SIZE][26]; int fail[SIZE]; bool isend[SIZE]; LL mat[SIZE][SIZE]; LL ans[SIZE][SIZE]; void mat_add(LL a[][SIZE],LL b[][SIZE]) { for(int i=0;i<node;i++) { for(int j=0;j<node;j++) a[i][j]+=b[i][j]; } } void mat_mul(LL a[][SIZE],LL b[][SIZE]) { LL tmp[SIZE][SIZE]; for(int i=0;i<node;i++) { for(int j=0;j<node;j++) { tmp[i][j]=0; for(int k=0;k<node;k++) tmp[i][j]+=a[i][k]*b[k][j]; } } memcpy(a,tmp,sizeof(mat)); } void mat_pow(LL a[][SIZE],LL b[][SIZE],int k) { memset(a,0,sizeof(mat)); for(int i=0;i<node;i++) a[i][i]=1; LL mm[SIZE][SIZE]; memcpy(mm,b,sizeof(mat)); while(k) { if(k&1) { mat_mul(a,mm); } k>>=1; mat_mul(mm,mm); } } void pow_sum(LL a[][SIZE],LL b[][SIZE],int k) { if(k==1) { memcpy(a,b,sizeof(mat)); return; } LL c[SIZE][SIZE],d[SIZE][SIZE]; pow_sum(a,b,k>>1); memcpy(d,a,sizeof(mat)); mat_pow(c,b,k>>1); mat_mul(a,c); mat_add(a,d); if(k&1) { mat_mul(c,c); mat_mul(c,b); mat_add(a,c); } } void init() { node=1; memset(next[0],0,sizeof(next[0])); } void add(int cur,int k) { memset(next[node],0,sizeof(next[node])); isend[node]=0; next[cur][k]=node++; } void insert(char *s) { int i,cur,k; for(i=cur=0;s[i];i++) { k=s[i]-'a'; if(!next[cur][k]) add(cur,k); cur=next[cur][k]; } isend[cur]=1; } void get_fail() { queue<int>q; int cur,nxt,tmp; fail[0]=0; q.push(0); while(!q.empty()) { cur=q.front(),q.pop(); for(int k=0;k<26;k++) { nxt=next[cur][k]; if(nxt) { if(!cur) fail[nxt]=0; else { for(tmp=fail[cur];tmp && !next[tmp][k];tmp=fail[tmp]); fail[nxt]=next[tmp][k]; } if(isend[fail[nxt]]) isend[nxt]=1; q.push(nxt); } else next[cur][k]=next[fail[cur]][k]; } } } void get_mat() { memset(mat,0,sizeof(mat)); for(int i=0;i<node;i++) { if(isend[i]) continue; for(int k=0;k<26;k++) { int j=next[i][k]; if(!isend[j]) mat[i][j]++; } } } void solve() { get_fail(); get_mat(); pow_sum(ans,mat,l); LL ret=0; for(int i=0;i<node;i++) ret-=ans[0][i]; mat[0][0]=26; node=1; pow_sum(ans,mat,l); ret+=ans[0][0]; printf("%I64u\n",ret); } int main() { char s[LEN]; while(~scanf("%d%d",&n,&l)) { init(); for(int i=0;i<n;i++) { scanf("%s",s); insert(s); } solve(); } return 0; }