bzoj 1030: [JSOI2007]文本生成器 (ac自动机上的dp)

1030: [JSOI2007]文本生成器

Time Limit: 1 Sec  Memory Limit: 162 MB

Submit: 2635  Solved: 1090

[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

ac自动机上的dp

dp[0][i][j]表示到第i位,以第j个状态结束不符合条件的串的个数

dp[1][i][j]表示到第i位,以第j个状态结束符合条件的串的个数

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 6005;
const int MOD = 10007;

int que[N], fr, ta;

struct ACM {
	int cnt;
	int nxt[N][26], sum[N], fail[N];
	void init() {
		for(int i = 1; i <= cnt; ++i) {
			sum[i] = fail[i] = 0;
			for(int j = 0; j < 26; ++j)
				nxt[i][j] = 0;
		}
		cnt = 1;
		for(int i = 0; i < 26; ++i) nxt[0][i] = 1;
	}
	void insert(string str) {
		int now = 1;
		int len = str.length();
		for(int i = 0; i < len; ++i) {
			if(nxt[now][str[i] - 'A'] == 0) nxt[now][str[i] - 'A'] = ++cnt;
			now = nxt[now][str[i] - 'A'];
		}
		sum[now] = 1;
	}
	void build_fail() {
		fr = ta = 0;
		que[ta++] = 1;
		fail[1] = 0;
		while(fr != ta) {
			int now = que[fr++];
			for(int i = 0; i < 26; ++i) {
				int x = nxt[now][i];
				if(x == 0) continue;
				int tmp = fail[now];
				while(nxt[tmp][i] == 0) tmp = fail[tmp];
				fail[x] = nxt[tmp][i];
				que[ta++] = x;
			}
		}
	}
	void debug() {
		for(int i = 1; i <= cnt; ++i) {
			cout<<i<<": "<<"fail = "<<fail[i]<<" [ ";
			for(int j = 0; j < 26; ++j) {
				if(nxt[i][j]) cout<<char(j + 'A')<<','<<nxt[i][j]<<' ';
			}
			puts("]");
		}
	}
	void gao(int, int);
} acm;

int dp[2][105][N];

void ACM::gao(int n, int m) {
	build_fail();
	//debug();
	memset(dp, 0, sizeof(dp));
	dp[0][0][1] = 1;
	for(int i = 1; i <= m; ++i) {
		for(int j = 1; j <= cnt; ++j) {
			int u = j;
			for(int k = 0; k < 26; ++k) {
				int v = u;
				while(nxt[v][k] == 0) v = fail[v];
				v = nxt[v][k];
				if(sum[v]) {
					dp[1][i][v] = (dp[1][i][v] + dp[1][i - 1][u] + dp[0][i - 1][u]) % MOD;
					dp[0][i][v] += 0;
				} else {
					dp[1][i][v] = (dp[1][i][v] + dp[1][i - 1][u]) % MOD;
					dp[0][i][v] = (dp[0][i][v] + dp[0][i - 1][u]) % MOD;
				}
			}
		}
	}
	int ans = 0;
	for(int i = 1; i <= cnt; ++i)
		ans = (ans + dp[1][m][i]) % MOD;
	cout<<ans<<endl;
}

string str[65];

bool ok(int x, int n) {
	int len = str[x].length();
	for(int i = 0; i < n; ++i) {
		if(i == x) continue;
		int pos = str[x].find(str[i]);
		if(pos >= 0 && pos < len) return false;
	}
	return true;
}

int main() {
	int n, m;
	cin>>n>>m;
	acm.init();
	for(int i = 0; i < n; ++i) {
		cin>>str[i];
	}
	sort(str, str + n);
	n = unique(str, str + n) - str;
	for(int i = 0; i < n; ++i) {
		if(ok(i, n)) acm.insert(str[i]);
	}
	acm.gao(n, m);
	return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-04 20:09:17

bzoj 1030: [JSOI2007]文本生成器 (ac自动机上的dp)的相关文章

bzoj 1030: [JSOI2007]文本生成器 (ac自己主动机上的dp)

1030: [JSOI2007]文本生成器 Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 2635  Solved: 1090 [Submit][Status][Discuss] Description JSOI交给队员ZYX一个任务.编制一个称之为"文本生成器"的电脑软件:该软件的使用者是一些低幼人群,他们如今使用的是GW文本生成器v6版.该软件能够随机生成一些文章―――总是生成一篇长度固定且全然随机的文章-- 也就是说,生成的文章中

BZOJ 1030: [JSOI2007]文本生成器( AC自动机 + dp )

之前一直没调出来T^T...早上刷牙时无意中就想出错在哪里了... 对全部单词建AC自动机, 然后在自动机上跑dp, dp(i, j)表示匹配到了第i个字符, 在自动机上的j结点的方案数, 然后枚举A~Z进行转移. -------------------------------------------------------------------------- #include<bits/stdc++.h> using namespace std; #define idx(c) ((c) -

bzoj 1030 [JSOI2007]文本生成器(AC自动机+DP)

1030: [JSOI2007]文本生成器 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 3059  Solved: 1255[Submit][Status][Discuss] Description JSOI交给队员ZYX一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,他们现在使用的是GW文本生成器v6版.该软件可以随机生成一些文章―――总是生成一篇长度固定且完全随机的文章—— 也就是说,生成的文章中每个字节都是完

BZOJ 1030 [JSOI2007]文本生成器(AC自动机)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1030 [题目大意] 求出包含任意一个给定串的串数量 [题解] 我们求出不包含任意一个给定串的数量,用全集去减即可, 对于给定串建立AC自动机,用1节点作为根,0节点向1连全字符集转移作为超级源, 那么0->match能匹配所有不包含给定串的串, 记dp[i][j]表示匹配了i长度,匹配到AC自动机j节点的串数量, 统计之后取补集即可. [代码] #include <cstdio&g

bzoj 1030: [JSOI2007]文本生成器

AC自动机的fail,然后用fail搞搞DP,不懂不懂,,,,啊啊啊啊啊啊 1 #include<bits/stdc++.h> 2 #define N 1000005 3 #define LL long long 4 #define inf 0x3f3f3f3f 5 using namespace std; 6 inline int ra() 7 { 8 int x=0,f=1; char ch=getchar(); 9 while (ch<'0' || ch>'9') {if (

1030: [JSOI2007]文本生成器

1030: [JSOI2007]文本生成器 https://www.lydsy.com/JudgeOnline/problem.php?id=1030 分析: AC自动机+dp. 正难则反,求满足的,可以求出不满足的,用总的减去.所以考虑如何就出所有的长度为m的串里,没有出现任何一个单词的个数. 建立AC自动机,然后会有一些点是一定不能走的,这些点要么是某些单词的结尾,或者是包含了某些单词(以它结尾的串的后缀是一个单词). 然后f[i][j]表示当前有多少位,在AC自动机的哪个位置的方案数.枚举

poj--1625Censored!+AC自动机上的dp+大数

题目链接:点击进入 其实看起来是完全可以用矩阵做的,但是因为用到了大数的,导致内存开不下,所以用dp写了.其实dp的过程依旧就是在我们用禁止出现单词构建的trie上走m步的过程.我们定义dp[i][j]表示走过i步以后到达节点j的方案数,则状态转移应该是dp[i][j]=sum(dp[i-1][k]),其中k表示可以走到j的节点,并且不能是病毒节点.但是其实这样代码就不是那么好写了,其实我们可以用节点j主动的去更新它的子节点k,这样转移方程就成了dp[i][next[j][k]]+=dp[i-1

【BZOJ1030】[JSOI2007]文本生成器 AC自动机+动态规划

[BZOJ1030][JSOI2007]文本生成器 Description JSOI交给队员ZYX一个任务,编制一个称之为"文本生成器"的电脑软件:该软件的使用者是一些低幼人群,他们现在使用的是GW文本生成器v6版.该软件可以随机生成一些文章―――总是生成一篇长度固定且完全随机的文章-- 也就是说,生成的文章中每个字节都是完全随机的.如果一篇文章中至少包含使用者们了解的一个单词,那么我们说这篇文章是可读的(我们称文章a包含单词b,当且仅当单词b是文章a的子串).但是,即使按照这样的标准

【题解】Luogu P4052[JSOI2007]文本生成器 AC自动机

AC自动机上DP f[i][j]表示节点j,串长为i 当一个串的尾节点被标红或其fail指针指向的被标红,都是可读的 用总的减去不可读的即为答案 #include<iostream> #include<cstring> #include<cstdio> #include<queue> #define MOD (10007) #define N (10005) using namespace std; int Son[N][26],End[N],Fail[N]