hdu2825-(AC自动机+状压DP)

题解:AC自动机,然后在自动机上跑一遍DP,第一维表示长度,第二维表示到达自动机上的哪一个结点,第三维用二进制表示有含有哪几个字符;然后转移方程就是

dp[i+1][ret][k|v[ret]] = (dp[i+1][ret][k|v[ret]]+dp[i][j][k])%mod;

有些dp[i][j][k]等于0就不用运算了

#include<iostream>
#include<cstring>
#include<set>
#include<algorithm>
#include<cstdio>
#include<queue>
using namespace std;
const int mod = 20090717;
int n,m,k;
struct tried{
    int dp[30][105][1<<10];
    int ch[105][26];
    int f[105];
    int v[105];
    int sz;
    void init(){
        memset(f,0,sizeof(f));
        memset(ch[0],0,sizeof(ch[0]));
        memset(v,0,sizeof(v));
        memset(dp,0,sizeof(dp));
        dp[0][0][0] = 1;
        sz = 1;
    }
    int idx(char c){
        return c-'a';
    }
    int num(int x){
        int ans = 0;
        while(x){
            if(x&1) ans++;
            x /= 2;
        }
        return ans;
    }
    void insert(char *s,int id){
        int u = 0,len = strlen(s);
        for(int i = 0; i < len; i++){
            int d = idx(s[i]);
            if(!ch[u][d]){
                memset(ch[sz],0,sizeof(ch[sz]));
                ch[u][d] = sz++;
            }
            u = ch[u][d];
        }
        v[u] = 1<<id;
    }
    void getfail(){
        int u = 0;
        queue<int>q;
        for(int d = 0; d < 26; d++)
            if(ch[u][d])
                q.push(ch[u][d]);
        while(!q.empty()){
            u = q.front();
            q.pop();
            v[u] |= v[f[u]];
            for(int d = 0; d < 26; d++){
                if(!ch[u][d]){
                    ch[u][d] = ch[f[u]][d];
                    continue;
                }
                int ret = ch[u][d];
                f[ret] = ch[f[u]][d];
                q.push(ret);
            }
        }
    }
    void work(){
        for(int i = 0; i < n; i++)
            for(int j = 0; j < sz; j++)
                for(int k = 0; k < (1<<m); k++)
                    if(dp[i][j][k])
                    for(int d = 0; d < 26; d++){
                        int ret = ch[j][d];
                        dp[i+1][ret][k|v[ret]] = (dp[i+1][ret][k|v[ret]]+dp[i][j][k])%mod;
                    }
        int ans = 0;
        for(int i = 0; i < sz; i++)
            for(int j = 0; j < (1<<m); j++)
                if(dp[n][i][j]&&num(j)>=k)
                    ans = (ans+dp[n][i][j])%mod;
        printf("%d\n",ans);
    }
}word;
char s[105];
int main(){
    while(scanf("%d%d%d",&n,&m,&k)!=EOF){
        if(n==0&&m==0&&k==0)
            break;
        word.init();
        for(int i = 0; i < m; i++){
            scanf("%s",s);
            word.insert(s,i);
        }
        word.getfail();
        word.work();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值