POJ2778 DNA Sequence AC自动机上dp

网址:https://vjudge.net/problem/POJ-2778

题意:

给出字符集${A,C,G,T}$和一些字符串(长度不超过$10$,且数量不超过$10$个),求长度为$n(n \leq 2e9)$的字符串中不包括上面这些字符串的字符串的数量。

题解:

我们可以先考虑一种方式:设$dp(i,j)$是用了$i$个字符拼出符合题意的长度为$j$的字符串的数量,在本题中$dp(i,j)=\sum _{j‘ \subseteq j} dp(i-1,j‘)$,显然时间复杂度是指数级的,不可能通过题目。

因为本题是多模式匹配,所以显然使用$AC$自动机。$AC$自动机的结束结点就是不可到达结点,然后因为AC自动机上某个节点的$fail$结点表示这个字符串的前缀子串的后缀串的结点。所以如果它的$fail$结点不可达,那么这个结点一定不可达。然后我们把$AC$自动机变成一张用邻接矩阵表示的有向图,设图上结点数为$p$。

然后以下介绍两种常见的$dp$策略:

一、我们考虑$dp(i,j)$是从$Trie$的根走$i$步到编号为$j$的结点的方案数,则有$dp(i,j)=\sum _{!tr[k].flag} dp(i-1,k)$ $(tr[k].flag==true \leftrightarrow k$点不可达$)$,这时就可以在$n$比较小但是矩阵很大的时候以时间复杂度为$O(np^2)$计算出来。显然在这个题不太行。

二、我们考虑在有向图中求从$s$点出发$n$步是否能到达$t$点的求法是通过将矩阵乘$n$次求出来,这一过程可以使用矩阵快速幂加速。时间复杂度是$O(p^3logn)$,本题可以通过。

AC代码:

#include <cstdio>
#include <cstring>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
const int N = 105;
const int mod = 100000;
map<char, int>mp;
int trie[N][4];
int fail[N];
bool f[N];
struct Mat
{
	ll mat[N][N];
	Mat()
	{
		memset(mat, 0, sizeof(mat));
	}
};
char s[15];
int cnt;
void insert(char* s, int len)
{
	int root = 0;
	for (int i = 0; i < len; ++i)
	{
		int nxt = mp[s[i]];
		if (!trie[root][nxt])
			trie[root][nxt] = ++cnt;
		root = trie[root][nxt];
	}
	f[root] = 1;
}
void getfail()
{
	queue<int>q;
	for (int i = 0; i < 4; ++i)
		if (trie[0][i])
			q.push(trie[0][i]), fail[trie[0][i]] = 0;
	while (!q.empty())
	{
		int now = q.front();
		q.pop();
		if (f[fail[now]])
			f[now] = 1;
		for (int i = 0; i < 4; ++i)
		{
			if (trie[now][i])
			{
				q.push(trie[now][i]);
				fail[trie[now][i]] = trie[fail[now]][i];
			}
			else
				trie[now][i] = trie[fail[now]][i];
		}
	}
}
Mat mul(const Mat& a, const Mat& b, int n)
{
	Mat res;
	for (int i = 0; i <= n; ++i)
		for (int j = 0; j <= n; ++j)
			for (int k = 0; k <= n; ++k)
				(res.mat[i][j] += a.mat[i][k] * b.mat[k][j]) %= mod;
	return res;
}
Mat pow(Mat a, int n, ll p)
{
	Mat res;
	for (int i = 0; i <= n; ++i)
		res.mat[i][i] = 1;
	while (p)
	{
		if (p & 1)
			res = mul(res, a, n);
		a = mul(a, a, n);
		p >>= 1;
	}
	return res;
}
int dp[N][N];
void solve(ll n)
{
	Mat ans;
	for (int i = 0; i <= cnt; ++i)
	{
		if (f[i])
			continue;
		for (int j = 0; j < 4; ++j)
		{
			if (f[trie[i][j]])
				continue;
			ans.mat[i][trie[i][j]] += 1;
		}
	}
	ans = pow(ans, cnt, n);
	ll tot = 0;
	for (int i = 0; i <= cnt; ++i)
		(tot += ans.mat[0][i]) %= mod;
	printf("%lld\n", tot);
}
int main()
{
	mp[‘A‘] = 0;
	mp[‘C‘] = 1;
	mp[‘G‘] = 2;
	mp[‘T‘] = 3;
	int n;
	ll m;
	scanf("%d%lld", &n, &m);
	for (int i = 1; i <= n; ++i)
	{
		scanf("%s", s);
		insert(s, strlen(s));
	}
	getfail();
	solve(m);
	return 0;
}

$*$吐槽一下,都0202年了POJ还不支持C++11和万能头

原文地址:https://www.cnblogs.com/Aya-Uchida/p/12325588.html

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

POJ2778 DNA Sequence AC自动机上dp的相关文章

[poj2778]DNA Sequence(AC自动机+矩阵快速幂)

解题关键:卡时限过的,正在找原因中. 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<cstring> 6 #include<iostream> 7 #include<queue> 8 using namespace std; 9 typedef long long ll; 10 cons

URAL 1158 AC自动机上的简单DP+大数

题目大意 在一种语言中的字母表中有N(N<=50)个字母,每个单词都由M(M<=50)个字母构成,因此,一共可以形成N^M个单词.但是有P(P<=10)个串是被禁止的,也就是说,任何单词的子串都不能包含这P个串中的任意一个.问按照上述规则,能产生的合法串一共有多少个? 例如:N=3 M=3 P=3 字母表中的三个字符是QWE 被禁止的串为”QQ”,”WEE”,”Q”,则合法的串一共有7个. 这题目相当于通过步数对AC自动机上每一个点的状态进行DP dp[i][j]表示到达i这个点,走了j

UVA 11468(Substring-AC自动机上dp)[Template:AC自动机]

Substring Given a set of pattern strings, and a text, you have to find, if any of the pattern is a substring of the text. If any of the pattern string can be found in text, then print "yes", otherwise "no" (without quotes). But, unfort

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

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版.该软件可以随机生成一些文章―――总是生成一篇长度固定且完全随机的文章-- 也就是说,生成的文章中

[POJ2778]DNA Sequence

看到n<=20亿顿时傻眼..AC自动机上用矩阵乘法优化DP...sxbk 建出AC自动机,把非法的节点去掉后求出trie图... 然后根据trie图中的转移关系建矩阵....最后跑个快速幂 竟然搞出来了...感人肺腑 脑子各种短路..先是矩乘打挂,然后是trie图求措TAT.调了一整节晚自修. 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm>

hdu 3992 AC自动机上的高斯消元求期望

Crazy Typewriter Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 391    Accepted Submission(s): 109 Problem Description There was a crazy typewriter before. When the writer is not very sober, it

POJ 2778 DNA Sequence (AC自动机,矩阵乘法)

题意:给定n个不能出现的模式串,给定一个长度m,要求长度为m的合法串有多少种. 思路:用AC自动机,利用AC自动机上的节点做矩阵乘法. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<string> 6 #include<algorithm> 7 #include<queue> 8 #defin

POJ POJ 2778 DNA Sequence AC自动机 + 矩阵快速幂

首先建立Trie和失败指针,然后你会发现对于每个节点 i 匹配AGCT时只有以下几种情况: i 节点有关于当前字符的儿子节点 j 且安全,则i 到 j找到一条长度为 1的路. i 节点有关于当前字符的儿子节点 j 且 不安全,则i 到 j没有路. i 节点没有关于当前字符的儿子节点 但是能通过失败指针找到一个安全的节点j,那么 i 到 j 找到一条长度为1的路. 关于节点安全的定义: 当前节点不是末节点且当前节点由失败指针指回跟节点的路径上不存在不安全节点,那么这个节点就是安全节点. 然后问题就