1. 程式人生 > >[AC自動機+DP] 限定長度不包含給定子串的可能字串數量 BZOJ1030

[AC自動機+DP] 限定長度不包含給定子串的可能字串數量 BZOJ1030

1030: [JSOI2007]文字生成器

Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 6242  Solved: 2656 [Submit][Status][Discuss]

Description

  JSOI交給隊員ZYX一個任務,編制一個稱之為“文字生成器”的電腦軟體:該軟體的使用者是一些低幼人群, 他們現在使用的是GW文字生成器v6版。該軟體可以隨機生成一些文章―――總是生成一篇長度固定且完全隨機的文 章—— 也就是說,生成的文章中每個位元組都是完全隨機的。如果一篇文章中至少包含使用者們瞭解的一個單詞, 那麼我們說這篇文章是可讀的(我們稱文章a包含單詞b,當且僅當單詞b是文章a的子串)。但是,即使按照這樣的 標準,使用者現在使用的GW文字生成器v6版所生成的文章也是幾乎完全不可讀的?。ZYX需要指出GW文字生成器 v6 生成的所有文字中可讀文字的數量,以便能夠成功獲得v7更新版。你能幫助他嗎?

Input

  輸入檔案的第一行包含兩個正整數,分別是使用者瞭解的單詞總數N (<= 60),GW文字生成器 v6生成的文字固 定長度M;以下N行,每一行包含一個使用者瞭解的單詞。這裡所有單詞及文字的長度不會超過100,並且只可能包 含英文大寫字母A..Z

Output

  一個整數,表示可能的文章總數。只需要知道結果模10007的值。

Sample Input

2 2 A B

Sample Output

100

#include <bits/stdc++.h>
using namespace std;

const int mn = 60 * 100 + 10;
const int mod = 10007;

int dp[105][mn];

int num;
int nx[mn][26], fail[mn];
bool ed[mn];
void add(char a[])
{
	int r = 0;
	int len = strlen(a);
	for (int i = 0; i < len; i++)
	{
		if (nx[r][a[i] - 'A'] == -1)
			nx[r][a[i] - 'A'] = ++num;
		r = nx[r][a[i] - 'A'];
	}
	ed[r] = 1;
}
void build_Fail()
{
	queue<int>que;
	int root = 0;
	fail[root] = root;
	for (int i = 0; i < 26; i++)
	{
		if (nx[root][i] == -1)
			nx[root][i] = root;
		else
		{
			fail[nx[root][i]] = root;
			que.push(nx[root][i]);
		}
	}
	while (!que.empty())
	{
		int now = que.front();
		que.pop();
		
		if (ed[fail[now]])  /// *****
			ed[now] = 1;
			
		for (int i = 0; i < 26; i++)
		{
			if (nx[now][i] == -1)
				nx[now][i] = nx[fail[now]][i];
			else
			{
				fail[nx[now][i]] = nx[fail[now]][i];
				que.push(nx[now][i]);
			}
		}
	}
}

int DP(int m) /// 第i步跳到j節點的可能情況數量
{
	dp[0][0] = 1;
	for (int i = 0; i < m; i++)
	{
		for (int j = 0; j <= num; j++)
		{
			for (int k = 0; k < 26; k++)
			{
				if (ed[nx[j][k]])
					continue;
				(dp[i + 1][nx[j][k]] += dp[i][j]) %= mod;
			}
		}
	}

	int ans = 0;
	for (int i = 0; i <= num; i++)
		(ans += dp[m][i]) %= mod;
	return ans;
}
int main()
{
	memset(nx, -1, sizeof nx);

	int n, m; 
	char ch[110];
	scanf("%d %d", &n, &m);
	while (n--)
	{
		scanf("%s", ch);
		add(ch);
	}
	build_Fail();

	int temp = DP(m);
	int res = 1;
	for (int i = 1; i <= m; i++)
		(res *= 26) %= mod;
	printf ("%d\n", (res - temp + mod) % mod);
	/// 總情況 - 不包含
	return 0;
}