【AC自动机】西征罗马之路

水题再没有爱了。。。


西征罗马之路(roma.c/cpp/pas) 2s 128MB


【背景】
    一个偶然的机会(niabby出题),Bob穿越到了三国时期,凭借天时,地利,人和以及修改器,Bob十年就成功统一了大陆版图。接下来的五年中,Bob不仅在南方平定了山越、南蛮之乱,东征邪马台而胜,在西部更是将版图扩张到了已被罗马帝国征服的古波斯一带。安定了内部环境后,为了争夺欧亚大陆的唯一王座,也为了国家的荣耀,Bob率领的精锐军团即将和罗马帝国现任皇帝Alice,the grace of god,在戈兰高地发动争夺天下霸权的霸者之战。
    Bob的军队中有机动力极高的轻骑兵,不动如山的步兵方阵,而弓弩和南蛮的战象军团也为主力的一部分。虽然对自己的军队战力很有信心,但Bob深知,要征服一个民族就要从文化入手。于是Bob大力鼓动军队将领学习拉丁文。
    为简化问题,以下用大写英文字母代替拉丁文。


【任务】
    经过一段时间的学习,将领们已经掌握了一些拉丁文词汇。Bob知道现在已经到了学习拉丁文的瓶颈期,他决定让将领们阅读一些固定长度的无空格的拉丁文文章。但是,如果将领阅读文章时,不能在其中找到任何一个自己已经掌握的词汇,那么他会非常伤心,觉得自己无能。相反,只要找到了一个自己认识的单词,将领们就会欣喜若狂,信心大涨(哪怕事实上和将领已知单词相同的那一段拉丁文并不能解释为那个单词,即将领“断章取义”)。Bob要培养将领学习拉丁文的信心。他想知道,如果知道了将领掌握的单词数量和一个固定的文章长度,能写出多少篇激励将领信心的文章。


【输入格式】
    输入文件input.txt第一行包含两个正整数n,m,其中n表示将领掌握的单词总数,m表示要写的文章的固定长度(文章只可能有大写英文字母,不含空格)
    接下来是n行由大写英文字母组成的字符串,每一个都是将领掌握的单词。


【输出格式】
    输出文件output.txt仅有一行,包含一个非负整数,表示激励将领信心的文章的总数模19980515的值


【输入样例】
1 1
A


【输出样例】
1


【数据范围】
    对于30%的数据,满足1<=n<=10,1<=m<=10
    对于100%的数据,满足1<=n<=100,1<=m<=100,将领掌握的单词长度不超过100


【备注】
    为了不(you)说(xi)废(ping)话(heng),出题人决定不给东方军团加入重骑兵部队
    多么具有代表性的样例啊!


#include <cstdio>
#include <iostream>
#include <queue>

using namespace std;

int read()
{
	int n = 0, sign = 1; char c = getchar();
	while(c < '0' || c > '9') {if(c == '-') sign = -1; c = getchar(); }
	while(c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }
	return n * sign;
}

inline void print(int x)
{
	if(x < 0) { putchar('-'); x *= -1; }
	if(!x) puts("0");
	else {
		int bit[20], len = 0;
		while(x) { bit[++len] = x % 10; x /= 10; }
		for(int i = len; i; --i) putchar(bit[i] + '0');
		puts("");
	}
}

const int size = 26;
const int Mod = 19980515;
const int maxn = 10005;

int N, M;
char st[105];

namespace AcAutomation {
	
	int tot, dp[105][maxn];
	
	struct trie{
		trie *ch[size], *fail;
		int num, w;
	}t[maxn], *root;
	
	inline trie *NewNode() { t[++tot].num = tot; return &t[tot]; }
	inline void init() { tot = -1; root = NewNode(); }
	
	inline void insert(char *s)
	{
		trie *p = root;
		while(*s)
		{
			int idx = *s - 'A';
			if(!p -> ch[idx]) p -> ch[idx] = NewNode();
			p = p -> ch[idx]; ++s;
		}
		p -> w = 1;
	}
	
	queue <trie*> q;
	
	inline void build()
	{
		for (int i = 0; i < size; ++i) if (root -> ch[i])
		{
			root -> ch[i] -> fail = root;
			q.push(root -> ch[i]);
		} else root -> ch[i] = root;
		
		while(q.size())
		{
			trie *p = q.front(); q.pop();
			for (int i = 0; i < size; ++i)
			{
				trie *son = p -> ch[i], *fail = p -> fail;
				if (son) 
				{
					q.push(son); son -> fail = fail -> ch[i];
					son -> w |= son -> fail -> w;
				} 
				else p -> ch[i] = fail -> ch[i];
			}
		}
	}
	
	inline void update(int &a, int &b) { a += b; while (a > Mod) a -= Mod; }
	
	inline int count()
	{
		dp[0][0] = 1;
		
		for (int step = 0; step < M; ++step)
		{
			int next = step + 1;
			for (int i = 0; i <= tot; ++i) 
			{
				trie *p = &t[i]; if (p -> w) continue;
				for (int j = 0; j < size; ++j) 
					update(dp[next][p -> ch[j] -> num], dp[step][i]); 
			}
		}
		
		int ans = 0;
		for (int i = 0; i <= tot; ++i) if (!t[i].w) update(ans, dp[M][i]);
		return ans;
	}
	
}

inline int Pow(int a, int b)
{
	int temp = 1, cmp = a;
	while (b)
	{
		if (b & 1) temp = ((long long)temp * cmp) % Mod;
		b >>= 1;
		cmp = ((long long)cmp * cmp) % Mod;
	}
	return temp;
}

int main()
{
	freopen("roma.in", "r", stdin);
	freopen("roma.out", "w", stdout);
	
	N = read(), M = read();
	
	using namespace AcAutomation; init();
	for(int i = 1; i <= N; ++i) { scanf("%s", st); insert(st); }
	build(); int temp = count();
	
	print((Pow(size, M) + Mod - temp) % Mod);
	
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值