hdu 2243 考研路茫茫——单词情结

题目传送门

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

题解

此题是poj2778的升级。我们可以先求出不含词根的单词个数,再用总数减去即可。由于长度是小于等于L,所以矩阵A'=A^1 + A^2 + A^3 + A^4 + …… + A^L,总数=26 + 26^2 + 26^3 + 26^4 + 26^5 +……+26^L = (26^(L+1)-26)/25。所以不同点就是求出矩阵A'。其求法可用折半法来求(可参照poj3233)。
Ps:题中L<2^32,又由于要使用L+1,所以应用long long储存。至于取模则直接用unsigned long long 自然溢出即可。
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

//AC auto

const int MS = 33, Siz = 26;
int ch[MS][Siz], Fail[MS], root, totN;
bool is_R[MS];

int NewNode() {
	is_R[++totN] = 0;
	memset(ch[totN], 0, sizeof ch[totN]);
	return totN;
}

void Insert(char *S) {
	int cur = root, idx;
	while (*S) {
		idx = *S - 'a';
		if (!ch[cur][idx]) ch[cur][idx] = NewNode();
		cur = ch[cur][idx];
		++S;
	}
	is_R[cur] = 1;
}

void BuildFail() {
	queue<int>q;
	Fail[root] = root;
	for (int i = 0; i < Siz; ++i) if (ch[root][i]) {
		Fail[ch[root][i]] = root;
		q.push(ch[root][i]);
	} 
	int p, f;
	while (!q.empty()) {
		p = q.front(); q.pop();
		if (is_R[Fail[p]]) is_R[p] = 1;
		for (int i = 0; i <Siz; ++i)
		if (ch[p][i]) {
			f = Fail[p];
			
			while (!ch[f][i] && f) f = Fail[f];
			
			if (ch[f][i]) Fail[ch[p][i]] = ch[f][i];
			else Fail[ch[p][i]] = root;
			
			q.push(ch[p][i]);
			
		} else ch[p][i] = ch[Fail[p]][i];		
	}
}

//Matix
typedef unsigned long long LL;
struct Mat {
	LL mp[MS][MS];
	void clear() {
		memset(mp, 0, sizeof mp);
	}
	Mat() { this->clear(); }
	
	Mat operator + (const Mat &A) const {
		Mat C;
		for (int i = 0; i < MS; ++i)
		for (int j = 0; j < MS; ++j)
		C.mp[i][j] = mp[i][j] + A.mp[i][j];
		return C;
	}
	
	Mat operator * (const Mat &A) const {
		Mat C;
		for (int i = 0; i < MS; ++i)
		for (int j = 0; j < MS; ++j)
		for (int k = 0; k < MS; ++k)
		C.mp[i][j] += mp[i][k] * A.mp[k][j];
		return C;
	}
	
	Mat operator ^ (const int &c) const {
		int k = c;
		Mat B = *this, C;
		for (int i = 0; i < MS; ++i) C.mp[i][i] = 1;
		while (k) {
			if (k & 1) C = C * B;
			B = B * B;
			k >>= 1;
		}
	}
	//求<span style="font-size:18px;">A^1 + A^2 + A^3 + A^4 + …… + A^L</span>
	Mat operator << (const LL &c) const {
		int i;
		for (i = 0; (1 << i) <= c; ++i);
		Mat C, B;
		C = B = *this;
		for (i -= 2; ~i; --i) {
			C = C + C * B;
			B = B * B;
			if ((1 << i) & c) B = B * (*this), C = C + B;
		}
		return C;
	}	
	
};

void BuildMat(Mat &A) {
	A.clear();
	for (int i = 0; i <= totN; ++i)
	for (int j = 0; j < Siz; ++j)
	if (!is_R[i] && !is_R[ch[i][j]]) ++A.mp[i][ch[i][j]];
}

const LL inr = 10330176681277348905ULL;//25的逆元

char tmpS[10];

LL mod_pow(LL base, LL n) {
	LL ans = 1;
	while (n) {
		if (n & 1) ans = ans * base;
		base = base * base;
		n >>= 1;
	}
	return ans;
}

int main() {
	int N;
	LL L,tot;
	Mat ans;
	while (scanf("%d", &N) == 1) {
		cin >> L;
		totN = -1; root = NewNode();
		while (N--) {
			scanf("%s", tmpS);
			Insert(tmpS);
		}
		BuildFail();
		BuildMat(ans);
		ans = ans << L;
		tot = mod_pow(26,(LL)L + 1) - 26;
		tot = tot * inr;
		for (int i = 0; i <= totN; ++i)
		tot -= ans.mp[0][i];
		cout << tot << endl;
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值