【HDU】 2243 考研路茫茫——单词情结 AC自动机+矩阵加速

考研路茫茫——单词情结

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实在是数不出来了,现在就请你帮帮他。
 

Input
本题目包含多组数据,请处理到文件结束。
每组数据占两行。
第一行有两个正整数N和L。(0<N<6,0<L<2^31)
第二行有N个词根,每个词根仅由小写字母组成,长度不超过5。两个词根中间用一个空格分隔开。
 

Output
对于每组数据,请在一行里输出一共可能的单词数目。
由于结果可能非常巨大,你只需要输出单词总数模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 )
具体加速方法见代码。

代码如下:

#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


 
 
 
 
 
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值