zoukankan      html  css  js  c++  java
  • 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
    ", 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和万能头

  • 相关阅读:
    为php5.6.30安装redis扩展
    Laravel利用pusher推送消息
    php闭包使用例子
    利用反射给类中方法加钩子
    mysql删除重复记录
    DB门面,查询构建器,Eloquent ORM三者的CURD
    Laravel5.1之表单验证
    服务提供者及门面
    批量搜索并替换内容
    Laravel之Elixir
  • 原文地址:https://www.cnblogs.com/Aya-Uchida/p/12325588.html
Copyright © 2011-2022 走看看