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][j]。要求next[j][k]不能使病毒节点。总而言之,AC自动机上的dp就是要利用我们建立的trie树以及上面的转移方式进行状态转移。

代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<map>
using namespace std;

///大数模版
struct BigInteger{
    int A[25];
    enum{MOD = 10000};
    BigInteger(){memset(A, 0, sizeof(A)); A[0]=1;}
    void set(int x){memset(A, 0, sizeof(A)); A[0]=1; A[1]=x;}
    void print(){
        printf("%d", A[A[0]]);
        for (int i=A[0]-1; i>0; i--){
            if (A[i]==0){printf("0000"); continue;}
            for (int k=10; k*A[i]<MOD; k*=10) printf("0");
            printf("%d", A[i]);
        }
        printf("\n");
    }
    int& operator [] (int p) {return A[p];}
    const int& operator [] (int p) const {return A[p];}
    BigInteger operator + (const BigInteger& B){
        BigInteger C;
        C[0]=max(A[0], B[0]);
        for (int i=1; i<=C[0]; i++)
            C[i]+=A[i]+B[i], C[i+1]+=C[i]/MOD, C[i]%=MOD;
        if (C[C[0]+1] > 0) C[0]++;
        return C;
    }
    BigInteger operator * (const BigInteger& B){
        BigInteger C;
        C[0]=A[0]+B[0];
        for (int i=1; i<=A[0]; i++)
            for (int j=1; j<=B[0]; j++){
                C[i+j-1]+=A[i]*B[j], C[i+j]+=C[i+j-1]/MOD, C[i+j-1]%=MOD;
            }
        if (C[C[0]] == 0) C[0]--;
        return C;
    }
}One;

const int maxn=10*10+5;
char alph[maxn];
map<char,int>h;

int getIndex(char ch)
{
     return h[ch];
}

struct Trie
{
    int next[maxn][maxn/2],fail[maxn],flag[maxn];
    int root,L,len;
    int newnode()
    {
        memset(next[L],-1,sizeof(next[L]));
        flag[L++];
        return L-1;
    }
    void init()
    {
        L=0;
        root=newnode();
    }
    void insert(char buf[])
    {
        int len1=strlen(buf);
        int now=root;
        for(int i=0;i<len1;i++)
        {
            int index=getIndex(buf[i]);
            if(next[now][index]==-1)
               next[now][index]=newnode();
            now=next[now][index];
        }
        flag[now]=1;
    }
    void build()
    {
        queue<int>Q;
        fail[root]=root;
        for(int i=0;i<len;i++)
        {
          if(next[root][i]==-1)
             next[root][i]=root;
          else
          {
              fail[next[root][i]]=root;
              Q.push(next[root][i]);
          }
        }
        while(!Q.empty())
        {
            int now=Q.front();
            if(flag[fail[now]])
               flag[now]++;
            Q.pop();
            for(int i=0;i<len;i++)
            {
                if(next[now][i]==-1)
                  next[now][i]=next[fail[now]][i];
                else
                {
                    fail[next[now][i]]=next[fail[now]][i];
                    //flag[next[now][i]]|=flag[next[fail[now]][i]];
                    Q.push(next[now][i]);
                }
            }
        }
    }
};


Trie ac;
char str[maxn];

BigInteger dp[maxn][maxn];

int main()
{
    int n,m,p;
    ///freopen("in.txt","r",stdin);
    One.set(1);
    while(scanf("%d%d%d",&n,&m,&p)!=EOF)
    {
        h.clear();
        scanf("%s",alph);
        for(int i=0;i<strlen(alph);i++)
           h[alph[i]]=i;
        ac.init();
        ac.len=strlen(alph);
        for(int i=0;i<p;i++)
        {
            scanf("%s",str);
            ac.insert(str);
        }
        ac.build();
        for(int i=0;i<=m;i++)
          for(int j=0;j<ac.L;j++)
             dp[i][j].set(0);
        dp[0][0].set(1);
        for(int i=1;i<=m;i++)
          for(int j=0;j<ac.L;j++)
            for(int k=0;k<n;k++)
            {
                if(ac.flag[ac.next[j][k]]) continue;
                dp[i][ac.next[j][k]]=dp[i][ac.next[j][k]]+dp[i-1][j];
            }
        BigInteger ans;
        ans.set(0);
        for(int i=0;i<ac.L;i++)
          ans=ans+dp[m][i];
        ans.print();
    }
  return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值