考研路茫茫——单词情结
自动机+DP+快速幂取模+矩阵快速幂。这个题目和POJ2778很相似,不过这个是求出包含的有多少个。也就是用总的个数减去不包含的。由于长度是1~L内的,所以我们得把所有的情况都给求出来。首先是求构成单词的总的个数,也就是:26^1 + 26^2 + 26^3 +....+ 26^L,当然我们可以将其写成更一般的形式:26*(26^L -1)/25,但是这样计算有很大的问题,由于取模运算对除法的未定义,我们必须保存26^L-1的值, 然后在做除法,当L很大的时候26^L很难保存下来,同时还要用这个很大的数去做除法,很难写。我们可以就直接按照所给的式子来计算:对于上述的式子,我们是可以采用二分来计算的,举个例子:26^1+26^2+26^3+26^4+26^5+26^6,这个式子我们可以这样写:
26^3(26^1+26^2+26^3)+(26^1+26^2+26^3),然后我们就只需要计算(26^1+26^2+26^3)*(26^3 + 1),这样我们的计算次数就得到简化。如此二分计算就可以了。这个题的取模是比较特殊的,由于是对2^64取模,化为二进制位:取模也就是对于二进制位在0~63的会保存下来,而高位就不用管了,因为高与63位的一定是2^64的倍数。所以我们在计算的时候不需要刻意的去做取模运算,因为高位会因存储不下而溢出,保留的肯定是地位。对于每一种不含给定字符的情况,我们可以像POJ2778那样,先计算出状态转移矩阵A,然后就是A + A^2 + A^3 +....+A^L,对于这个式子我们一样的采用二分的方式,进行快速计算,POJ3233就是求这个式子,计算方式一样。
/*
Author: csuchenan
LANG: c++
Algorithm: AC + DP + Martix
*/
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std ;
typedef unsigned long long ll ;
struct Martix{
ll mat[28][28] ;
};
Martix E_const ;
int next[30][26] ;
int fail[30] ;
bool flag[30] ;
int cnt ;
int N ;
int L ;
void init(){
memset(next , 0 , sizeof(next)) ;
memset(fail , 0 , sizeof(fail)) ;
memset(flag , 0 , sizeof(flag)) ;
cnt = 0 ;
memset(E_const.mat , 0 , sizeof(E_const.mat)) ;
for(int i = 0 ; i < 28 ;i ++){
E_const.mat[i][i] = 1 ;
}
}
void insert(char * str){
char *p = str ;
int b ;
int c(0) ;
while(*p){
b = *p - 'a' ;
if( !next[c][b] ){
next[c][b] = ++ cnt ;
}
c = next[c][b] ;
p ++ ;
}
flag[c] = 1 ;
}
bool read(){
if(scanf("%d%d" , &N , &L)==EOF)
return 0 ;
init() ;
char str[8] ;
for(int k = 1 ; k <= N ; k ++){
scanf("%s" , str) ;
insert(str) ;
}
return 1 ;
}
void build_ac(){
queue<int> Q ;
int cur(0) ;
int child ;
int k ,tmp ;
fail[cur] = 0 ;
Q.push(cur) ;
while(!Q.empty()){
cur = Q.front() ;
Q.pop() ;
for(k = 0 ; k < 26 ; k ++){
child = next[cur][k] ;
if(child){
Q.push(child) ;
if(!cur){
fail[child] = 0 ;
}
else{
tmp = fail[cur] ;
for( ;tmp && !next[tmp][k] ; tmp = fail[tmp])
;
if(next[tmp][k])
fail[child] = next[tmp][k] ;
else
fail[child] = 0 ;
}
if(flag [ fail[child] ] )
flag[child] = 1 ;
}
else{
next[cur][k] = next[ fail[cur] ][k] ;
}
}
}
}
Martix build_Martix(){
Martix E ;
memset(E.mat , 0 , sizeof(E.mat)) ;
for(int i = 0 ; i <= cnt ; i ++){
if(flag[i])
continue ;
for(int j = 0 ; j < 26 ; j ++){
int cur = next[i][j] ;
if(flag[cur])
continue ;
E.mat[cur][i] ++ ;
}
}
return E ;
}
Martix Martix_Add(Martix A , Martix B){
Martix ans ;
memset(ans.mat , 0 , sizeof(ans.mat)) ;
for(int i = 0 ; i <= cnt ; i ++){
for(int j = 0 ; j <= cnt ; j ++){
ans.mat[i][j] = A.mat[i][j] + B.mat[i][j] ;
}
}
return ans ;
}
Martix Martix_Mul(Martix A , Martix B){
Martix ans ;
memset(ans.mat , 0 , sizeof(ans.mat)) ;
ll sum ;
for(int i = 0 ; i <= cnt ; i ++){
for(int j = 0 ; j <= cnt ; j ++){
sum = 0 ;
for(int k = 0 ; k <= cnt ; k ++){
sum += A.mat[i][k] * B.mat[k][j] ;
}
ans.mat[i][j] = sum ;
}
}
return ans ;
}
Martix power_Martix(Martix E , int k){
Martix ret = E_const ;
while(k){
if(k&1)
ret = Martix_Mul(ret , E) ;
E = Martix_Mul(E , E) ;
k = k >>1 ;
}
return ret ;
}
Martix Sum_Martix(Martix E , int k){
Martix ans ;
if(k == 1)
return E ;
Martix P = power_Martix(E , k>>1) ;
Martix S = Sum_Martix(E , k>>1) ;
ans = Martix_Mul(S , Martix_Add(P , E_const)) ;
if(k&1){
ans = Martix_Add(ans , power_Martix(E , k)) ;
}
return ans ;
}
ll power(ll x , int k ){
ll ret(1) ;
while(k){
if(k&1)
ret = ret * x ;
x = x * x ;
k = k >> 1 ;
}
return ret ;
}
ll Sum(ll x , int k){
if(k == 1 )
return x ;
if(k&1)
return Sum(x , k>>1)*( power(x , k>>1) + 1) + power(x , k) ;
return Sum(x , k >>1)*( power(x , k>>1) + 1) ;
}
void solve(){
build_ac() ;
Martix M = build_Martix() ;
Martix F = Sum_Martix(M , L) ;
ll total = Sum(26 , L) ;
ll ans = 0 ;
for(int i = 0 ; i <= cnt ; i ++){
ans += F.mat[i][0] ;
}
ans = total - ans ;
if(ans < 0){
ans = ans + ( (ll)1<<63 ) + ( (ll)1<<63 ) ;
}
printf("%I64u\n" , ans) ;
}
int main(){
while(read()){
solve() ;
}
return 0 ;
}