考研路茫茫——单词情结
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3211 Accepted Submission(s): 921
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
Author
linle
传送门:【HDU】 2243 考研路茫茫——单词情结
题目分析:
建议先做这题:【POJ】2778 DNA Sequence
本题就是2278稍微变化了一下。既然要求一定包含,那么我们就转化成总数 减 一定不包含数就可以了。
题目要求对2^64取模,其实只要用unsigned long long 就可以自动取模了~
ans = 26^1 + 26^2 + 26^3 + ... + 26^n - ( A^1 + A^2 + A^3 + ... + A^n )
具体加速方法见代码。
代码如下:
传送门:【HDU】 2243 考研路茫茫——单词情结
题目分析:
建议先做这题:【POJ】2778 DNA Sequence
本题就是2278稍微变化了一下。既然要求一定包含,那么我们就转化成总数 减 一定不包含数就可以了。
题目要求对2^64取模,其实只要用unsigned long long 就可以自动取模了~
ans = 26^1 + 26^2 + 26^3 + ... + 26^n - ( A^1 + A^2 + A^3 + ... + A^n )
具体加速方法见代码。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
#define REP( I , N ) for ( int I = 0 ; I < N ; ++ I )
#define MOD 0
#define DEQUEUE 0
typedef unsigned long long ULL ;
typedef ULL type_Matrix ;//矩阵元素类型
typedef char type_buf ;//buf串类型
const int maxN = 64 ;//节点总数
const int maxW = 26 ;//分支总数
const int maxQ = 100000 ;//队列大小
#if MOD
const int mod = 1e9 + 7 ;
#endif
struct Matrix {
type_Matrix mat[maxN][maxN] ;
int N ;
Matrix () {}
Matrix ( int n ) {//矩阵初始化
N = n ;
REP ( i , N )
REP ( j , N )
mat[i][j] = 0 ;
}
Matrix operator + ( Matrix &A ) const {//重载矩阵加法
REP ( i , N )
REP ( j , N )
#if MOD
A.mat[i][j] = ( A.mat[i][j] + mat[i][j] ) % mod ;
#else
A.mat[i][j] += mat[i][j] ;
#endif
return A ;
}//+
Matrix operator * ( Matrix &A ) const {//重载矩阵乘法
Matrix res = Matrix ( N ) ;
REP ( i , N )
REP ( j , N )
REP ( k , N )
#if MOD
res.mat[i][j] = ( res.mat[i][j] + mat[i][k] * A.mat[k][j] ) % mod ;
#else
res.mat[i][j] += mat[i][k] * A.mat[k][j] ;
#endif
return res ;
}//*
void Init () {//矩阵单位化
REP ( i , N )
mat[i][i] = 1 ;
}//Init
void Build ( Matrix A ) {//构造二级矩阵
for ( int i = 0 ; i < A.N ; ++ i ) {
for ( int j = 0 ; j < A.N ; ++ j ) {
mat[i][j] = A.mat[i][j] ;
mat[i][j + A.N] = A.mat[i][j] ;
mat[i + A.N][j + A.N] = ( i == j ) ;
}
}
}//Build
} ;
struct Trie {
int next[maxN][maxW] ;
int fail[maxN] ;
int end[maxN] ;
int P , root ;
int Q[maxQ] ;
int head , tail ;
Trie () {}
int New () {//新节点
REP ( i , maxW ) next[P][i] = -1 ;
end[P] = 0 ;
return P ++ ;
}//New
void Init () {//初始化
P = 0 ;
root = New () ;
}//Init
int Get ( type_buf ch ) {//转化
return ch - 'a' ;
}//Get
void Insert ( type_buf buf[] , int len = 0 , int idx = 0 ) {//插入
int now = root ;
for ( int i = 0 ; buf[i] ; ++ i ) {
int x = Get ( buf[i] ) ;
if ( next[now][x] == -1 ) {
next[now][x] = New () ;
}
now = next[now][x] ;
}
end[now] = 1 ;
}//Insert
void Build () {//建立自动机
head = tail = 0 ;
fail[root] = root ;
REP ( i , maxW ) {
if ( next[root][i] == -1 ) {
next[root][i] = root ;
}
else {
fail[next[root][i]] = root ;
Q[tail ++] = next[root][i] ;
#if DEQUEUE
if ( tail >= maxQ ) tail -= maxQ ;
#endif
}
}
while ( head != tail ) {
int now = Q[head ++] ;
#if DEQUEUE
if ( head >= maxQ ) head -= maxQ ;
#endif
REP ( i , maxW ) {
if ( next[now][i] == -1 ) {
next[now][i] = next[fail[now]][i] ;
}
else {
fail[next[now][i]] = next[fail[now]][i] ;
end[next[now][i]] |= end[fail[next[now][i]]] ;
Q[tail ++] = next[now][i] ;
#if DEQUEUE
if ( tail >= maxQ ) tail -= maxQ ;
#endif
}
}
}
}//Build
Matrix Matrix_Build () {//构造矩阵
Matrix res ( P ) ;
REP ( i , P )
REP ( j , maxW )
if ( 0 == end[next[i][j]] )
++ res.mat[i][next[i][j]] ;
return res ;
}//Matrix_Build
} ;
Trie AC ;
type_buf buf[maxN] ;
Matrix Matrix_Pow ( Matrix A , int k ) {
Matrix res ( A.N ) , tmp = A ;
res.Init () ;
while ( k ) {
if ( k & 1 ) res = res * tmp ;
tmp = tmp * tmp ;
k >>= 1 ;
}
return res ;
}//Matrix_Pow
void Work () {
int n , k ;
while ( ~scanf ( "%d%d" , &n , &k ) ) {
AC.Init () ;//自动机初始化
REP ( i , n ) {
scanf ( "%s" , buf ) ;
AC.Insert ( buf ) ;
}
AC.Build () ;//建立自动机
Matrix A = AC.Matrix_Build () ;//构造一级矩阵
Matrix An ( A.N << 1 ) ;//创建二级矩阵
An.Build ( A ) ;//构造二级矩阵
An = Matrix_Pow ( An , k ) ;//矩阵快速幂求不符合串个数
/*============================================================================*
| |A A| |A^n A^n + A^(n-1) + A^(n-2) + ... + A | |
|An = | | ---> An^n = | | |
| |0 E| |0 E | |
*============================================================================*/
Matrix a ( 1 ) ;
a.mat[0][0] = maxW ;
Matrix an ( 2 ) ;
an.Build ( a ) ;
an = Matrix_Pow ( an , k ) ;//矩阵快速幂求所有串个数
/*============================================================================*
| |26 26| |26^n 26^n + 26^(n-1) + 26^(n-2) + ... + 26| |
|An = | | ---> An^n = | | |
| |0 1| |0 1 | |
*============================================================================*/
for ( int i = A.N ; i < An.N ; ++ i ) {//An.N = A.N << 1
an.mat[0][1] -= An.mat[0][i] ;
}
printf ( "%I64u\n" , an.mat[0][1] ) ;
}
}//Work
int main () {
Work () ;
return 0 ;
}//main