【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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章