POJ1625---Censored!(AC自动机+dp+高精度)

Description

The alphabet of Freeland consists of exactly N letters. Each sentence of Freeland language (also known as Freish) consists of exactly M letters without word breaks. So, there exist exactly N^M different Freish sentences.

But after recent election of Mr. Grass Jr. as Freeland president some words offending him were declared unprintable and all sentences containing at least one of them were forbidden. The sentence S contains a word W if W is a substring of S i.e. exists such k >= 1 that S[k] = W[1], S[k+1] = W[2], …,S[k+len(W)-1] = W[len(W)], where k+len(W)-1 <= M and len(W) denotes length of W. Everyone who uses a forbidden sentence is to be put to jail for 10 years.

Find out how many different sentences can be used now by freelanders without risk to be put to jail for using it.

Input

The first line of the input file contains three integer numbers: N – the number of letters in Freish alphabet, M – the length of all Freish sentences and P – the number of forbidden words (1 <= N <= 50, 1 <= M <= 50, 0 <= P <= 10).

The second line contains exactly N different characters – the letters of the Freish alphabet (all with ASCII code greater than 32).

The following P lines contain forbidden words, each not longer than min(M, 10) characters, all containing only letters of Freish alphabet.

Output

Output the only integer number – the number of different sentences freelanders can safely use.

Sample Input

2 3 1

ab

bb

Sample Output

5

Source

Northeastern Europe 2001, Northern Subregion

AC自动机+dp

dp[i][j]表示长度为i,在节点j满足条件的方案数

要用到高精度

注意给字符离散化的时候,由于存在ascll在128~255的,转换为int的时候为负,这里要注意

/*************************************************************************
  > File Name: POJ1625.cpp
  > Author: ALex
  > Mail: [email protected]
  > Created Time: 2015年03月09日 星期一 15时08分58秒
 ************************************************************************/

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double eps = 1e-15;
typedef long long LL;
typedef pair <int, int> PLL;

struct BigInt
{
    const static int mod = 10000;
    const static int DLEN = 4;
    int a[600], len;
    void clear()
    {
        memset (a, 0, sizeof(a));
    }

    BigInt()
    {
        memset (a, 0, sizeof(a));
    }

    BigInt(int v)
    {
        memset (a, 0, sizeof(a));
        len = 0;
        do
        {
            a[len++] = v % mod;
            v /= mod;
        }
        while (v);
    }

    BigInt operator + (const BigInt &b) const
    {
        BigInt res;
        res.len = max(len, b.len);
        for (int i = 0; i < res.len; ++i)
        {
            res.a[i] = 0;
        }
        for (int i = 0; i < res.len; ++i)
        {
            res.a[i] += ((i < len) ? a[i] : 0) + ((i < b.len) ? b.a[i] : 0);
            res.a[i + 1] += res.a[i] / mod;
            res.a[i] %= mod;
        }
        if (res.a[res.len] > 0)
        {
            ++res.len;
        }
        return res;
    }
    BigInt& operator = (const BigInt &b)
    {
        this -> len = b.len;
        for (int i = 0; i < b.len; ++i)
        {
            this -> a[i] = b.a[i];
        }
        return *this;
    }

    BigInt operator * (const BigInt &b)const
    {
        BigInt res;
        for (int i = 0; i < len; ++i)
        {
            int up = 0;
            for (int j = 0; j < b.len; ++j)
            {
                int tmp = a[i] * b.a[j] + res.a[i + j] + up;
                res.a[i + j] = tmp % mod;
                up = tmp / mod;
            }
            if (up != 0)
            {
                res.a[i + b.len] = up;
            }
        }
        res.len = len + b.len;
        while (res.a[res.len - 1] == 0 && res.len > 1)
        {
            --res.len;
        }
        return res;
    }

    void output()
    {
        printf("%d", a[len - 1]);
        for (int i = len - 2; i >= 0; --i)
        {
            printf("%04d", a[i]);
        }
        printf("\n");
    }
};

const int MAX_NODE = 110;

BigInt dp[2][MAX_NODE];
int CHILD_NUM;
map <char, int> HASH;

bool operator == (BigInt &c, BigInt &d)
{
    if (c.len != d.len)
    {
        return 0;
    }
    for (int i = 0; i < c.len; ++i)
    {
        if (c.a[i] != d.a[i])
        {
            return 0;
        }
    }
    return 1;
}

struct AC_Automation
{
    int next[MAX_NODE][55];
    int fail[MAX_NODE];
    bool end[MAX_NODE];
    int root, L;

    int ID(char c)
    {
        return HASH[c];
    }

    int newnode()
    {
        for (int i = 0; i < CHILD_NUM; ++i)
        {
            next[L][i] = -1;
        }
        end[L++] = 0;
        return L - 1;
    }

    void init()
    {
        L = 0;
        root = newnode();
    }

    void Build_Trie(char buf[])
    {
        int now = root;
        int len = strlen(buf);
        for (int i = 0; i < len; ++i)
        {
            if (next[now][ID(buf[i])] == -1)
            {
                next[now][ID(buf[i])] = newnode();
            }
            now = next[now][ID(buf[i])];
        }
        end[now] = 1;
    }

    void Build_AC()
    {
        queue <int> qu;
        fail[root] = root;
        for (int i = 0; i < CHILD_NUM; ++i)
        {
            if (next[root][i] == -1)
            {
                next[root][i] = root;
            }
            else
            {
                fail[next[root][i]] = root;
                qu.push(next[root][i]);
            }
        }
        while (!qu.empty())
        {
            int now = qu.front();
            qu.pop();
            if (end[fail[now]])
            {
                end[now] = 1;
            }
            for (int i = 0; i < CHILD_NUM; ++i)
            {
                if (next[now][i] == -1)
                {
                    next[now][i] = next[fail[now]][i];
                }
                else
                {
                    fail[next[now][i]] = next[fail[now]][i];
                    qu.push(next[now][i]);
                }
            }
        }
    }

    void solve (int n)
    {
        BigInt x(1);
        BigInt y;
        y.clear();
        for (int i = 0; i <= 1; ++i)
        {
            for (int j = 0; j < L; ++j)
            {
                dp[i][j].clear();
            }
        }
        dp[0][0] = x;
        for (int i = 1; i <= n; ++i)
        {
            for (int j = 0; j < L; ++j)
            {
                dp[i % 2][j].clear();
            }
            for (int j = 0; j < L; ++j)
            {
                for (int k = 0; k < CHILD_NUM; ++k)
                {
                    int nxt = next[j][k];
                    if (end[nxt])
                    {
                        continue;
                    }
                    dp[i % 2][nxt] = (dp[i % 2][nxt] + dp[1 - i % 2][j]);
                }
            }
        }
        BigInt ans(0);
        for (int i = 0; i < L; ++i)
        {
            if (end[i])
            {
                continue;
            }
            ans = (ans + dp[n % 2][i]);
        }
        ans.output();
    }
}AC;

char buf[20];
char alp[55];

int main ()
{
    int m, p;
    while (~scanf("%d%d%d", &CHILD_NUM, &m, &p))
    {
        AC.init();
        gets(alp);
        gets(alp);
        for (int i = 0; i < CHILD_NUM; ++i)
        {
                HASH[alp[i]] = i;
        }
        for (int i = 1; i <= p; ++i)
        {
            gets(buf);
            AC.Build_Trie(buf);
        }
        AC.Build_AC();
        AC.solve(m);
    }
    return 0;
}
时间: 2024-08-05 07:08:50

POJ1625---Censored!(AC自动机+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>

[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

POJ1625 Censored!(AC自动机+DP)

题目问长度m不包含一些不文明单词的字符串有多少个. 依然是水水的AC自动机+DP..做完后发现居然和POJ2778是一道题,回过头来看都水水的... dp[i][j]表示长度i(在自动机转移i步)且后缀状态为自动机第j个结点的合法字符串数 dp[0][0]=1 转移转移... 注意要用高精度,因为答案最多5050. 还有就是要用unsigned char,题目的输入居然有拓展的ASCII码,编码128-255. 1 #include<cstdio> 2 #include<cstring&

HDU3341 Lost&#39;s revenge(AC自动机+DP)

题目是给一个DNA重新排列使其包含最多的数论基因. 考虑到内存大概就只能这么表示状态: dp[i][A][C][G][T],表示包含各碱基个数为ACGT且当前后缀状态为自动机第i的结点的字符串最多的数论基因数 其中ACGT可以hash成一个整数(a*C*G*T+c*G*T+g*T+T),这样用二维数组就行了,而第二维最多也就11*11*11*11个. 接下来转移依然是我为人人型,我是丢进一个队列,用队列来更新状态的值. 这题果然挺卡常数的,只好手写队列,最后4500msAC,还是差点超时,代码也

HDU 2296 Ring AC自动机 + DP

题意:给你n个模式串,每个模式串有一个得分,让你构造出一个长度为N之内且分数最高的文本串;输出字典序列最小的. 解题思路:  AC自动机 + DP , 不过要输出字典序列最小,多开一个 一个三维字符串来辅助二维DP(新思路) , DP[i][j] ,表示到i位置状态为j的最大得分. 解题代码: 1 // File Name: temp.cpp 2 // Author: darkdream 3 // Created Time: 2014年09月11日 星期四 15时18分4秒 4 5 #inclu

HDU2296——Ring(AC自动机+DP)

题意:输入N代表字符串长度,输入M代表喜欢的词语的个数,接下来是M个词语,然后是M个词语每个的价值.求字符串的最大价值.每个单词的价值就是单价*出现次数.单词可以重叠.如果不止一个答案,选择字典序最小的. 题解:AC自动机+dp.dp[i][j]表示在字符串长度i,在自动机的第j个状态.因为要字典序最小,所以转移时要保持字典序最小. 想了各种转移姿势 最后还是查了题解 发现可以直接记录前缀转移…… #include <bits/stdc++.h> using namespace std; co

hdu 2296 aC自动机+dp(得到价值最大的字符串)

Ring Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3180    Accepted Submission(s): 1033 Problem Description For the hope of a forever love, Steven is planning to send a ring to Jane with a rom

hdu 2457 AC自动机+dp

DNA repair Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2004    Accepted Submission(s): 1085 Problem Description Biologists finally invent techniques of repairing DNA that contains segments c

HDU2296 Ring(AC自动机+DP)

题目是给几个带有价值的单词.而一个字符串的价值是 各单词在它里面出现次数*单词价值 的和,问长度不超过n的最大价值的字符串是什么? 依然是入门的AC自动机+DP题..不一样的是这题要输出具体方案,加个字符数组记录每个状态最优情况的字符串即可. 另外题目字典序是先考虑长度再考虑每一位单词:特别要注意,有一个非常坑的地方看了Disscus才知道——单词A包含单词B,那么只计算单词A不计算单词B. dp[i][j]表示长度i(自动机上转移k步)后缀状态是自动机第j个结点的字符串的最大价值 dp[0][