HDU2243 考研路茫茫——单词情结 AC自动机DP矩阵优化

传送门:点击打开链接

考研路茫茫——单词情结

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3929    Accepted Submission(s): 1147


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

题意:给出n个单词。问长度不超过m的串中,至少包含上述的至少一个单词的串的数目,对2^64取MOD

思路:这道题和POJ 2278比较相似,没有做过的请先参考http://blog.csdn.net/w703710691d/article/details/43271693

与POJ 2278最大的不同的,这道题要求得是长度不超过m的串。就要求得长度为1、2、3……m的符合要求串。可以参照POJ 2278的思路,先求出不符合的串,在用总数减去这个值,就得到了答案。下面设矩阵为A.中间会用到求A+A^2+A^3+……A^m【记为X】和26+26^2+26^3……+26^m次方的值。可以参考二分幂的方法。先求出(A+A^2+A^3……A^(m/2)【记为B】和A^(m/2)的值。那么X=(A^(m/2)+E)*B。E为单位阵。算数的也是同样的方法。将运算过程中的变量和矩阵的元素定义为unsigned __int64,就能实现自动对2^64自动取MOD

代码:

#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#define SIGMA_SIZE 26
#define maxn 40
#include<queue>
#define uLL unsigned __int64
using namespace std;
int ch[maxn][SIGMA_SIZE];
int val[maxn];
int last[maxn], f[maxn];
int cnt;
inline int idx(char c)
{
	return c - 97;
}
void insert(char s[])
{
	int len = strlen(s);
	int u = 0;
	for (int i = 0; i<len; i++)
	{
		int v = idx(s[i]);
		if (!ch[u][v]) ch[u][v] = ++cnt;
		u = ch[u][v];
	}
	val[u] = 1;
}
struct Matrix
{
	uLL g[maxn][maxn];
	int r, c;
	Matrix operator * (const Matrix &b) const
	{
		Matrix res;
		res.r = r;
		res.c = b.c;
		memset(res.g, 0, sizeof(res.g));
		for (int i = 0; i <= r; i++)
			for (int j = 0; j <= b.c; j++)
				for (int k = 0; k <= c; k++) res.g[i][j] += g[i][k] * b.g[k][j];
		return res;
	}
	Matrix operator + (const Matrix &b) const
	{
		Matrix res;
		res.r = r;
		res.c = c;
		memset(res.g, 0, sizeof(g));
		for (int i = 0; i <= r; i++)
			for (int j = 0; j <= c; j++) res.g[i][j] = g[i][j] + b.g[i][j];
		return res;
	}
};
Matrix ONE(int M)
{
	Matrix res;
	res.r = M;
	res.c = M;
	memset(res.g, 0, sizeof(res.g));
	for (int i = 0; i <= M; i++) res.g[i][i] = 1;
	return res;
}
Matrix ZERO(int M)
{
	Matrix res;
	res.r = M;
	res.c = M;
	memset(res.g, 0, sizeof(res.g));
	return res;
}
pair<Matrix, Matrix> bin(Matrix a, int k)
{
	pair<Matrix, Matrix> tmp, res;
	if (k == 0)
	{
		return make_pair(ZERO(a.r), ONE(a.r));
	}
	else if (k == 1)
		return make_pair(a, a);
	tmp = bin(a, k >> 1);
	res.first = (tmp.second + ONE(a.r))*tmp.first;
	res.second = tmp.second*tmp.second;
	if (k & 1)
	{
		res.second = res.second*a;
		res.first = res.first + res.second;
	}
	return res;
}
bool vis[maxn];
int mp[maxn][maxn];
void bfs()
{
	memset(vis, 0, sizeof(vis));
	vis[0] = 1;
	queue<int>q;
	q.push(0);
	while (!q.empty())
	{
		int u = q.front();
		q.pop();
		for (int i = 0; i<26; i++)
		{
			int p = u;
			while (p&&!ch[p][i]) p = f[p];
			p = ch[p][i];
			if (val[p]) continue;
			if (last[p]) continue;
			mp[u][p]++;
			if (!vis[p]) q.push(p);
			vis[p] = 1;
		}
	}
}
pair<uLL, uLL> bin(uLL a, int k)
{
	if (k == 1) return make_pair(a, a);
	else if (k == 0) return (make_pair(0, 1));
	pair<uLL, uLL>tmp, res;
	tmp = bin(a, k >> 1);
	res.first = (tmp.second + 1)*tmp.first;
	res.second = tmp.second*tmp.second;
	if (k & 1)
	{
		res.second = res.second*a;
		res.first = res.first + res.second;
	}
	return res;
}
void slove(uLL m)
{
	memset(mp, 0, sizeof(mp));
	bfs();
	Matrix tmp;
	tmp.r = cnt;
	tmp.c = cnt;
	for (int i = 0; i <= cnt; i++)
		for (int j = 0; j <= cnt; j++) tmp.g[i][j] = mp[i][j];
	pair<Matrix, Matrix> res;
	res = bin(tmp, m);
	uLL ans = 0;
	for (int i = 0; i <= cnt; i++) ans += res.first.g[0][i];
	ans = bin(26, m).first - ans;
	printf("%I64u\n", ans);
}
void getFail()
{
	queue<int>q;
	f[0] = 0;
	for (int c = 0; c<SIGMA_SIZE; c++)
	{
		int u = ch[0][c];
		if (u)
		{
			f[u] = 0;
			q.push(u);
			last[u] = 0;
		}
	}
	while (!q.empty())
	{
		int r = q.front();
		q.pop();
		for (int c = 0; c<SIGMA_SIZE; c++)
		{
			int u = ch[r][c];
			if (!u)
			{
				//ch[r][c] = ch[f[r]][c];
				continue;
			}
			q.push(u);
			int v = f[r];
			while (v&&!ch[v][c]) v = f[v];
			f[u] = ch[v][c];
			last[u] = val[f[u]] ? f[u] : last[f[u]];
		}
	}
}
int main()
{
	int n, m;
	while (~scanf("%d %d", &n, &m))
	{
		memset(ch, 0, sizeof(ch));
		memset(val, 0, sizeof(val));
		cnt = 0;
		char s[15];
		for (int i = 1; i <= n; i++)
		{
			scanf("%s", s);
			insert(s);
		}
		getFail();
		slove(m);
	}
	return 0;
}
可能会栈溢出,所以我直接交了C++



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
考研词汇5500 Excel版是一本非常重要的辅导资料,对于考研英语单词的掌握具有很大帮助。Excel版的设计使得学习者可以更加方便地进行复习和记忆。 首先,Excel版的结构清晰,以表格的形式呈现单词的意思、词性、例句等信息,使得学习者能一目了然地了解单词的各个方面。这样的设计让学习者能够更加高效地进行复习,提高学习效果。 其次,Excel版提供了各种排序和筛选的功能,学习者可以根据自己的需要进行自定义的排序和筛选,例如按字母顺序排序或者按词频筛选。这样的功能使得学习者可以根据自己的需求来选择复习的重点和方式,提高了学习的针对性。 另外,Excel版还提供了单词的音频功能,学习者可以通过听音频来提高自己的听力能力,同时加深对单词的记忆。这种结合听力的学习方式可以帮助学习者更好地掌握和记忆单词。 最后,Excel版还可以用来进行单词的测试和评估,学习者可以使用Excel版来进行自测,及时发现和纠正自己的不足之处,提高学习效果。 总之,考研词汇5500 Excel版是一本非常实用的辅导资料,它的清晰结构、自定义排序和筛选功能、音频功能以及测试评估功能,都为考研英语单词的学习提供了很大的帮助。学习者可以根据自己的需求和特点,灵活使用这本资料,提高自己的词汇量和考研英语能力。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值