HDU_2825 Wireless Password AC自动机+dp

http://acm.hdu.edu.cn/showproblem.php?pid=2825

题意:

给你M个最长长度为10的字符串,问有多少个仅由小写字母构成的,至少有

K个给定字符串的字符串。

思路:

AC自动机+dp。dp( i , j , k)表示长度为i的字符串,在trie中的第j个结点时,含

有的字符串的信息为k时候的种数。dp(i ,j , k ) = sum{ dp( i-1 ,j' ,k' )} ;这样的状态

转移方程的复杂度为:O(N*kind*2^M*node_num),其中kind为字符的取值域,

node_num为trie树中一共有多少个结点。

代码:

#include<stdio.h>
#include<string.h>
#include<queue>
const int Mod = 20090717 ;
int N ,M, K ;
struct Node{
    int fail ;
    int end ;
    int next[26] ;
    void init(){
        fail = -1 ;
        end = 0 ;
        memset(next, -1, sizeof(next));
    }
}p[110] ;
int  Root, cnt ;
void build(char *ch,int num){
    int loc , idx ,len = strlen(ch);
    loc = Root ;
    for(int i=0;i<len;i++){
        idx = ch[i] - 'a' ;
        if( p[loc].next[idx] == -1){
            ++cnt ;
            p[cnt].init() ;
            p[loc].next[idx] = cnt ;
        }
        loc = p[loc].next[idx] ;
    }
    p[loc].end |= (1<<(num-1)) ;
}
void build_ac(){
    int loc = Root ;
    p[loc].fail = -1 ;
    std::queue<int >  que ;
    while(!que.empty()) que.pop() ;
    que.push(loc) ;
    while(!que.empty()){
        int u = que.front() ; que.pop() ;
        for(int i=0;i<26;i++){
            int v = p[u].next[i] ;
            if(v == -1){
                if(u == Root)
                    p[u].next[i] = Root ;
                else
                    p[u].next[i] = p[ p[u].fail ].next[i] ;
            }
            else{
                int temp =  p[u].fail ;
                if(u == Root)
                    p[v].fail = Root ;
                else{
                    p[v].fail = p[temp].next[i] ;
                    p[v].end |= p[p[temp].next[i]].end ;
                }
                que.push(v) ;
            }
        }
    }
}
int dp[30][110][1030] ;
int map[1050] ;

void DP(){
    memset(dp , 0 ,sizeof(dp));
    dp[0][0][0] = 1 ;
    int MA = (1<<M) ;
    for(int j=0;j<MA;j++){
        map[j] = 0 ;
        for(int i=1;i<=M;i++)
            if( j&(1<<(i-1)) )  map[j] ++ ;
    }
    for(int i=1;i<=N;i++){
        for(int j=0;j<=cnt;j++){
            for(int k=0;k<MA;k++){
                if( dp[i-1][j][k] == 0 )    continue ;
                if( ( k & p[j].end ) != p[j].end )  continue ;
                for(int c=0;c<26;c++){
                    int v = p[j].next[c] ;
                    int kk = k | p[v].end ;
                    dp[i][v][kk] += dp[i-1][j][k] ;
                    if( dp[i][v][kk] >= Mod)    dp[i][v][kk] %= Mod ;
                }
            }
        }
    }
    int ans = 0 ;
    for(int j=0;j<=cnt;j++){
        for(int k=0;k<MA;k++){
            if(map[k] >= K)
                ans = ( ans + dp[N][j][k] ) % Mod  ;
        }
    }
    printf("%d\n",ans);
}
int main(){
    char ch[20] ;
    Root = 0 ;
    while(scanf("%d%d%d",&N,&M,&K) == 3){
        if(0==N && M==0 && K==0)    break ;
        p[Root].init() ; cnt = 0 ;
        for(int i=1;i<=M;i++){
            scanf("%s",ch);
            build(ch,i);
        }
        build_ac() ;
        DP() ;
    }
    return 0 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值