POJ 1625 Censored ( Trie图 && DP && 高精度 )

题意 : 给出 n 个单词组成的字符集 以及 p 个非法串,问你用字符集里面的单词构造长度为 m 的单词的方案数有多少种?

分析 :先构造出 Trie 图方便进行状态转移,这与在 POJ 2278 中的步骤是一样的,只不过最后的DP状态转移方式 2778 是利用了矩阵进行转移的,那是因为需要构造的串的长度非常长!只能利用矩阵转移。但是这道题需要构造的串的长度最多也就只有 50 ,可以利用普通的DP方法进行转移。我们定义 DP[i][j] 为以长度为 i 以字符 j 为结尾的串的种类数是多少,那么状态转移方程很显然就是 DP[i+1][k] += DP[i][j] * G[j][k] 这个方程表示现在 k 到 j 有一条边并且从k 走一步可以到 j 的方案数是 G[j][k] ( Trie 图构建出来的 ),那么现在 DP[i+1][k] 就很明显可以从 DP[i][j] 转移而来,DP的初始状态为 DP[0][0] = 0 && DP[0][i] = 0。

注意 :

① 因为没有要求对答案进行求模运算,答案可能很大,因为如果 p = 0,而n 和 m 都达到最大的50,那么答案就是 50^50,所以需要用到高精度。

② 字符可能有超过 128 的,也就是有负数情况,用map转化

#include<string.h>
#include<stdio.h>
#include<iostream>
#include<queue>
#include<map>
using namespace std;
const int Max_Tot = 111;
const int Letter = 256;
int G[111][111], n;
map<int, int> mp;
struct bign{
    #define MAX_B (100)
    #define MOD (10000)
    int a[MAX_B], n;
    bign() { a[0] = 0, n = 1; }
    bign(int num)
    {
        n = 0;
        do {
            a[n++] = num % MOD;
            num /= MOD;
        } while(num);
    }
    bign& operator= (int num)
    { return *this = bign(num); }
    bign operator+ (const bign& b) const
    {
        bign c = bign();
        int cn = max(n, b.n), d = 0;
        for(int i = 0, x, y; i < cn; i++)
        {
            x = (n > i) ? a[i] : 0;
            y = (b.n > i) ? b.a[i] : 0;
            c.a[i] = (x + y + d) % MOD;
            d = (x + y + d) / MOD;
        }
        if(d) c.a[cn++] = d;
        c.n = cn;
        return c;
    }
    bign& operator+= (const bign& b)
    {
        *this = *this + b;
        return *this;
    }
    bign operator* (const bign& b) const
    {
        bign c = bign();
        int cn = n + b.n, d = 0;
        for(int i = 0; i <= cn; i++)
            c.a[i] = 0;
        for(int i = 0; i < n; i++)
        for(int j = 0; j < b.n; j++)
        {
            c.a[i + j] += a[i] * b.a[j];
            c.a[i + j + 1] += c.a[i + j] / MOD;
            c.a[i + j] %= MOD;
        }
        while(cn > 0 && !c.a[cn-1]) cn--;
        if(!cn) cn++;
        c.n = cn;
        return c;
    }
    friend ostream& operator<< (ostream& _cout, const bign& num)
    {
        printf("%d", num.a[num.n - 1]);
        for(int i = num.n - 2; i >= 0; i--)
            printf("%04d", num.a[i]);
        return _cout;
    }
};
struct Aho{
    struct StateTable{
        int Next[Letter];
        int fail, flag;
    }Node[Max_Tot];
    int Size;
    queue<int> que;

    inline void init(){
        while(!que.empty()) que.pop();
        memset(Node[0].Next, 0, sizeof(Node[0].Next));
        Node[0].fail = Node[0].flag = 0;
        Size = 1;
    }

    inline void insert(char *s){
        int now = 0;
        for(int i=0; s[i]; i++){
            int idx = mp[s[i]];
            if(!Node[now].Next[idx]){
                memset(Node[Size].Next, 0, sizeof(Node[Size].Next));
                Node[Size].fail = Node[Size].flag = 0;
                Node[now].Next[idx] = Size++;
            }
            now = Node[now].Next[idx];
        }
        Node[now].flag = 1;
    }

    inline void BuildFail(){
        Node[0].fail = 0;
        for(int i=0; i<n; i++){
            if(Node[0].Next[i]){
                Node[Node[0].Next[i]].fail = 0;
                que.push(Node[0].Next[i]);
            }else Node[0].Next[i] = 0;///必定指向根节点
        }
        while(!que.empty()){
            int top = que.front(); que.pop();
            if(Node[Node[top].fail].flag) Node[top].flag = 1;
            for(int i=0; i<n; i++){
                int &v = Node[top].Next[i];
                if(v){
                    que.push(v);
                    Node[v].fail = Node[Node[top].fail].Next[i];
                }else v = Node[Node[top].fail].Next[i];
            }
        }
    }

    inline void BuildMap(){
        for(int i=0; i<Size; i++)
            for(int j=0; j<Size; j++)
                G[i][j] = 0;

        for(int i=0; i<Size; i++){
            for(int j=0; j<n; j++){
                if(!Node[ Node[i].Next[j] ].flag)
                    G[i][Node[i].Next[j]]++;
            }
        }
    }
}ac;

#define MAX_M (55)
bign dp[MAX_M][Max_Tot];

char s[51];
int main(void)
{
    int m, p;
    while(~scanf("%d %d %d\n", &n, &m, &p)){
        mp.clear();
        gets(s);
        int len = strlen(s);
        for(int i=0; i<len; i++)
            mp[s[i]] = i;

        ac.init();
        for(int i=0; i<p; i++){
            gets(s);
            ac.insert(s);
        }
        ac.BuildFail();
        ac.BuildMap();

        for(int i=0; i<=m; i++)
            for(int j=0; j<ac.Size; j++)
                dp[i][j] = bign();

        dp[0][0] = 1;
        for(int i=0; i<m; i++)
        for(int j=0; j<ac.Size; j++){
            for(int k=0; k<ac.Size; k++){
                dp[i+1][k] += dp[i][j] * G[j][k];
            }
        }

        bign ans = bign();

        for(int i=0; i<ac.Size; i++)
            ans += dp[m][i];

        cout<<ans<<endl;
    }
    return 0;
}

原文地址:https://www.cnblogs.com/shuaihui520/p/11615523.html

时间: 2024-10-08 14:23:19

POJ 1625 Censored ( Trie图 && DP && 高精度 )的相关文章

poj 1625 Censored!(AC自动机+DP+高精度)

题目链接:poj 1625 Censored! 题目大意:给定N,M,K,然后给定一个N字符的字符集和,现在要用这些字符组成一个长度为M的字符串,要求不包 括K个子字符串. 解题思路:AC自动机+DP+高精度.这题恶心的要死,给定的不能匹配字符串里面有负数的字符情况,也算是涨姿势 了,对应每个字符固定偏移128单位. #include <cstdio> #include <cstring> #include <queue> #include <vector>

POJ 1625 Censored! (AC自动机 + 高精度 + DP)

题目链接:Censored! 解析:AC自动机 + 高精度 + 简单DP. 字符有可能会超过128,用map映射一下即可. 中间的数太大,得上高精度. 用矩阵快速幂会超时,简单的DP就能解决时间的问题. AC代码: #include <iostream> #include <string.h> #include <algorithm> #include <stdio.h> #include <queue> #include <map>

POJ 1625 Censored!(AC自动机,DP)

http://poj.org/problem?id=1625 Censored! Time Limit: 5000MS   Memory Limit: 10000K Total Submissions: 8266   Accepted: 2229 Description The alphabet of Freeland consists of exactly N letters. Each sentence of Freeland language (also known as Freish)

hdu2457 Trie图+dp

hdu2457 给定n个模式串, 和一个文本串 问如果修改最少的字符串使得文本串不包含模式串, 输出最少的次数,如果不能修改成功,则输出-1 dp[i][j] 表示长度为i的字符串, 到达状态j(Trie图中的结点)所需要修改的最少次数 那么dp[0->n][0->size] = INF ,  dp[0][root] = 0,  n代表字符串长度, size代表状态数 那么答案就是  min{dp[n][size]} 我们根据模式串所建的Trie图, 进行模拟构造不包含模式串的字符串 从第一个

DNA Sequence POJ - 2778 邻接矩阵 trie图 矩阵快速幂

首先构造trie图. 我们明确一点的是,给出trie图,那么所有点的转移方式都是唯一可以确定的.即使是没有这个字符,他也会指向根节点. 我们根据离散数学的知识可以知道.计算有向图的邻接矩阵,然后k次方,就能够计算出从某一个点到另一个点,有多少条长度为k的路径. 故,我们构造出来trie图,拿出该图的邻接矩阵,就能计算路径数目.--(注意改图是有向图)-- trie图的构造不说了,模板. 邻接矩阵的构造根据trie图来的.我们在trie图上找到每一个节点,查看他的相邻节点,即A,G,C,T四个点指

POJ 1625 Censored! (AC自己主动机 + 高精度 + DP)

题目链接:Censored! 解析:AC自己主动机 + 高精度 + 简单DP. 字符有可能会超过128.用map映射一下就可以. 中间的数太大.得上高精度. 用矩阵高速幂会超时,简单的DP就能解决时间的问题. AC代码: #include <iostream> #include <string.h> #include <algorithm> #include <stdio.h> #include <queue> #include <map&

[AC自动机+dp+高精度] poj 1625 Censored!

题意: 给一个长度为N的字符串,表示有N个字符可用. 再给p个不能含有的病毒串. 为你长度为M的串不含有任意一个病毒串的方案数. 思路: 由于不需要取模,50^50需要用到高精度. 因为题目的字符串大于32所以可以直接scanf输入. 输入完map一下就好了. 就是裸的自动机dp了. 代码: #include"stdio.h" #include"algorithm" #include"string.h" #include"iostrea

poj 1625 Censored!

Censored! http://poj.org/problem?id=1625 Time Limit: 5000MS   Memory Limit: 10000K       Description The alphabet of Freeland consists of exactly N letters. Each sentence of Freeland language (also known as Freish) consists of exactly M letters witho

poj 3635 Full Tank? ( 图上dp )

题意: 已知每个点的加油站的油价单价(即点权),每条路的长度(边权). 有q个询问,每个询问包括起点s.终点e和油箱容量. 问从起点走到终点的最小花费.如果不可达输出impossible,否则输出最小的旅途费用. 算法: 其实要分析状态= =感觉就像是dp. 最直接的想法是  每到一个点都加上要走到下一个点所需要的油量.但是走的路不同,到底怎么处理加多少的问题呢? 因此想到分解状态,即拆点.每到一个点都+1单位的油量,然后把这个状态加入队列.另外如果现在油箱内的油足够达到下一点, 则更新状态,把