HDU-2825Wireless Password(AC自動機+狀壓DP)

HDU-2825

題意:多組輸入,每組給定n,m,k三個整數,一個長度爲n的字符串,由m個子字符串中的至少k個組成,求一共有多少種排列組合方法,結果需要mod

解題思路:

ac自動機上跑狀壓DP

dp[i+1][nx][k|val[nx]] = (dp[i+1][nx][k|val[nx]]+dp[i][j][k])%mod

第一維表示當前主串長度,第二維表示自動機上的節點,第三維用二進制表示集合中存在哪幾個字符串。需要利用輔助數組num預處理二進制下數字x的1的個數,即包含字符串的個數

代碼:

//#pragma GCC optimize(3)
#include<iostream>
#include<algorithm>
#include<vector>
#include<deque>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<string>
#include<sstream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cctype>
#include<cassert>
#include<cstdlib>
#include<utility>
#include<iterator>
#include<iomanip>

using namespace std;
#define lowbit(x) x&(-x)
typedef long long ll;
typedef long double lb;
typedef unsigned long long ull;
int read();
const double PI = acos(-1.0);
const int maxn = 110;
const int mod = 20090717;
int n,m,cnt;
int tot;
struct node
{
    int nx[26];
    int val;
    int fail;
}ac[maxn];
int num[1<<11];
int dp[30][maxn][1<<10];//長度,節點,狀態
int ans;
char s[50];
void init()
{
    tot = 0;
    for(int i = 0;i<maxn;i++)
    {
        memset(ac[i].nx,0,sizeof(ac[i].nx));
        ac[i].fail = 0;
        ac[i].val = 0;
    }
}
void getnum()
{
    for(int i = 0;i<(1<<10);i++)
    {
        num[i] = 0;
        for(int j = 0;j<10;j++)
        {
            if(i&(1<<j))
            {
                num[i]++;
            }
        }
    }
}
void insert(char *s,int id)
{
    int p = 0;
    int len = strlen(s);
    for(int i = 0; i<len; i++)
    {
        int ch = s[i]-'a';
        if(!ac[p].nx[ch])
        {
            ac[p].nx[ch] = ++tot;
        }
        p = ac[p].nx[ch];
    }
    ac[p].val|=(1<<id);
}
void getfail()
{
    queue<int> q;
    for(int i = 0;i<26;i++)
    {
        if(ac[0].nx[i])
        {
            ac[ac[0].nx[i]].fail = 0;
            q.push(ac[0].nx[i]);
        }
    }
    while(!q.empty())
    {
        int r = q.front();
        q.pop();
        ac[r].val|=ac[ac[r].fail].val;
        for(int i = 0;i<26;i++)
        {
            if(!ac[r].nx[i])
            {
                ac[r].nx[i] = ac[ac[r].fail].nx[i];
                
            }else
            {
                ac[ac[r].nx[i]].fail = ac[ac[r].fail].nx[i];
                q.push(ac[r].nx[i]); 
            }

        }
    }
}
int gao()
{
    memset(dp,0,sizeof(dp));
    dp[0][0][0] = 1;
    for(int i = 0;i<n;i++){
        for(int j = 0;j<=tot;j++){
            for(int k = 0;k<(1<<m);k++){
                  if(dp[i][j][k])
                  {
                      for(int c = 0;c<26;c++)
                      {
                         int nowi = i+1;
                         int nowj = ac[j].nx[c];
                         int nowk = k|ac[nowj].val;
                         dp[nowi][nowj][nowk] += dp[i][j][k];
                         dp[nowi][nowj][nowk]%=mod;
                      }
                  }
            }
        }
    }
    ans = 0;
    for(int i =0;i<(1<<m);i++)
    {
        if(num[i]>=cnt)
        {
            for(int j = 0;j<=tot;j++)
            {
                ans = (ans+dp[n][j][i])%mod;
            }
        }
    }
    return ans%mod;
}
int main() {

    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    getnum();
    while(scanf("%d%d%d",&n,&m,&cnt)!=EOF)
    {
        if(n==0&&m==0&&cnt==0)break;
        init();
        for(int i = 0;i<m;i++)
        {
            scanf("%s",&s);
            insert(s,i);
        }
        getfail();
        printf("%d\n",gao());
    }
    
    
    return 0;
}

inline int read() {
    int x = 0, w = 0;
    char ch = 0;
    while (!isdigit(ch)) {
        w |= (ch == '-');
        ch = getchar();
    }
    while (isdigit(ch)) {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return w ? -x : x;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章