poj 1625 Censored! (ac自动机+dp)

题意:

一个神奇的国度他们字母类型有n个,并且每句话必须是m个字母组成的,但是国王很奇葩,他设定了一些单词,只要句子中出现这样的单词那么造句的人就好入狱。给出了p个不能出现的单词。问有多少种句子可以出现。

题解:

ac自动上的dp,第一次做参照了bin神的代码,很神奇啊,ac自动机上也可以dp 0 0!

好了我以自己理解来分析这题。

首先,我们要将所给的n个字符进行处理,因为这n个字符有可能不是以阿斯科马值相邻的,那么我们用map离散化。

接着,我们将限制的串插入自动机中,凡是出现end[i]==0的就是可行的串。

然后,建ac自动机时,要注意一点,如果适配指针对应的节点是限制串(fail[i]==1)那么对应的这个指向适配指正的对应节点也应该是限制串,这个要好好理解下。

最后我们只要这样设置状态dp[step][i]走了step步,并且以i节点为结尾的合法句子个数。只要枚举上一步的节点是哪个即可。注意要排出限制的:if(end[i]==1)continue;

最后一点,答案太大,要用大数存。我懒得写,可耻的盗用了别人的大数类。


#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<map>
#include<set>
using namespace std;
#define B(x) (1<<(x))
typedef long long ll;
const int oo=0x3f3f3f3f;
const ll OO=1LL<<61;
const ll MOD=10007;
const int maxn=1005;
const int SIZE=110;
char buff[maxn];
map<char,int>mat;

struct AC
{
    int next[SIZE][256],fail[SIZE],end[SIZE],Q[SIZE*256];
    int root,cnt;

    void Init()
    {
        cnt=0;
        root=newNode();
    }

    int newNode()
    {
        for(int i=0;i<256;i++)
            next[cnt][i]=-1;
        end[cnt++]=0;
        return cnt-1;
    }

    void Insert(char buff[])
    {
        int now=root;
        int len=strlen(buff);
        for(int i=0,k;i<len;i++)
        {
            k=mat[buff[i]];
            if(next[now][k]==-1)
                next[now][k]=newNode();
            now=next[now][k];
        }
        end[now]=1;///这个单词是否存在
    }

    void build()
    {
        fail[root]=root;
        int front,rear;
        front=rear=0;
        int now=root;
        for(int i=0;i<256;i++)
        {
            if(next[now][i]==-1)
                next[now][i]=root;
            else
            {
                fail[next[now][i]]=root;
                Q[rear++]=next[now][i];
            }
        }
        while(front<rear)
        {
            now=Q[front++];
            if(end[fail[now]]) end[now]=1;///这句话非常重要,没加wa一发
            for(int i=0;i<256;i++)
            {
                if(next[now][i]==-1)
                    next[now][i]=next[fail[now]][i];
                else
                {
                    fail[next[now][i]]=next[fail[now]][i];
                    Q[rear++]=next[now][i];
                }
            }
        }
    }
/*
    void Search(char buff[])
    {
        int now=root;
        int len=strlen(buff);
        for(int i=0;i<len;i++)
        {
            now=next[now][buff[i]];
            int temp=now;
            while(temp!=root)
            {
                if(end[temp])
                    StrId.insert(end[temp]);
                ans[end[temp]]++;
                temp=fail[temp];
            }
        }
    }*/

    void Debug()
    {
        for(int i=0;i<cnt;i++)
        {
            printf("id=%3d fail=%3d end=%3d child=[",i,fail[i],end[i]);
            for(int j=0;j<26;j++)
                printf(" %3d",next[i][j]);
            printf(" ]\n");
        }
    }
};
AC ac;

///大数
struct BigInt
{
    const static int mod = 10000;
    const static int DLEN = 4;
    int a[600],len;
    BigInt()
    {
        memset(a,0,sizeof(a));
        len = 1;
    }
    BigInt(int v)
    {
        memset(a,0,sizeof(a));
        len = 0;
        do
        {
            a[len++] = v%mod;
            v /= mod;
        }while(v);
    }
    BigInt(const char s[])
    {
        memset(a,0,sizeof(a));
        int L = strlen(s);
        len = L/DLEN;
        if(L%DLEN)len++;
        int index = 0;
        for(int i = L-1;i >= 0;i -= DLEN)
        {
            int t = 0;
            int k = i - DLEN + 1;
            if(k < 0)k = 0;
            for(int j = k;j <= i;j++)
                t = t*10 + s[j] - '0';
            a[index++] = t;
        }
    }
    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)const
    {
        BigInt res;
        for(int i = 0; i < len;i++)
        {
            int up = 0;
            for(int j = 0;j < b.len;j++)
            {
                int temp = a[i]*b.a[j] + res.a[i+j] + up;
                res.a[i+j] = temp%mod;
                up = temp/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");
    }
};
BigInt dp[2][SIZE];

int main()
{
    int n,m,p;
    while(scanf("%d %d %d",&n,&m,&p)!=EOF)
    {
        scanf("%s",buff);
        mat.clear();
        for(int i=0;buff[i];i++)
            mat[buff[i]]=i;
        ac.Init();
        for(int i=1;i<=p;i++)
        {
            scanf("%s",buff);
            ac.Insert(buff);
        }
        ac.build();
        int now=0,pre=1;
        dp[now][0]=1;
        for(int i=1;i<ac.cnt;i++)
            dp[now][i]=0;
        for(int t=1;t<=m;t++)
        {
            now^=1;
            pre^=1;
            for(int i=0;i<ac.cnt;i++)
                dp[now][i]=0;
            for(int i=0;i<ac.cnt;i++)
            {
                if(ac.end[i])continue;///存在这样的路径跳过.
                for(int j=0;j<n;j++)
                {
                    int k=ac.next[i][j];
                    if(ac.end[k])continue;
                    dp[now][k]=dp[now][k]+dp[pre][i];
                }
            }
        }
        BigInt ans=0;
        for(int i=0;i<ac.cnt;i++)
            ans=ans+dp[now][i];
        ans.output();
    }
    return 0;
}
/**
5 5 5
abcde
bee
edcba
de
e
bbaed

ans=1024
*/




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值