链接: http://acm.hdu.edu.cn/showproblem.php?pid=2825
分类: AC自动机 + 状态压缩 + DP
题意:问构成长度为 N 的字符串中包含已知串中至少 K 个的总个数
分析:
AC自动机部分:采用朴素的构建即可,注意记录当前节点的状态(status)即到达当前节点时包含的已知串状态
状态压缩部分:采用位存储当前节点包含已知串状态,第 i 位为1,表示包含第 i 个字符串
DP部分:建立dp[ i ][ j ][ k ],表示长度为 i,状态到达第 j 个结点,且包含已知串状态为 k 时的个数,显然有 dp[ 0 ][ 0 ][ 0 ] = 1,最后的结果是 ans = sum{ dp[ N ][ j ][ k ] | Ones( k ) >= K },其中Ones( k )表示 k 的二进制数中包含 1 的个数。
假设处理长度为i,当前结点为 j,且包含已知串状态为 k,且即将转移到某一结点指针 p,那么有
dp[ i ][ p->idx ][ p->status | k ] = dp[ i ][ p->idx ][ p->status | k ] + dp[ i - 1 ][ j ][ k ]
参考程序
#include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> using namespace std; typedef long long i64d; const int MOD = 20090717; const int maxQ = 10086; const int maxC = 26; const int maxE = 1024; const int maxS = 1024; const int maxN = 30; struct node { node *fal, *cld[maxC]; int status, idx; }; static node *Q[maxQ]; static int Qs, Qt; static node E[maxE]; static int nE; static node *root; inline void init() { nE = -1; root = &E[++nE]; root->status = 0, root->idx = 0; root->fal = 0; memset(root->cld, 0, sizeof(root->cld)); } inline void insert(char str[], int idx) { static int pos; static node *curNode; curNode = root; for(int i = 0; str[i]; ++i) { pos = str[i] - 'a'; if( curNode->cld[pos] != 0) curNode = curNode->cld[pos]; else { curNode->cld[pos] = &E[++nE]; curNode = curNode->cld[pos]; curNode->fal = 0; memset(curNode->cld, 0, sizeof(curNode->cld)); curNode->status = 0, curNode->idx = nE; } } curNode->status |= (0x1 << idx); } inline void acmation() { root->fal = 0; // root's fail static int pos; Qs = Qt = 0; Q[Qt++] = root; static node *cur; while( Qs != Qt ) { cur = Q[Qs++]; for(int i = 0; i < maxC; ++i) { pos = i; // change if possible if( cur->cld[pos] != 0 ) { if( cur == root ) cur->cld[pos]->fal = root; else { cur->cld[pos]->fal = cur->fal->cld[pos]; cur->cld[pos]->status |= cur->cld[pos]->fal->status; } Q[Qt++] = cur->cld[pos]; } else { if( cur == root ) cur->cld[pos] = root; else cur->cld[pos] = cur->fal->cld[pos]; } } } } static int N, M, K; static char subString[30]; static int dp[maxN][maxE][maxS]; static int Ones[maxS]; inline void getOnes() { static int val; for(int i = 0; i < maxS; ++i) { Ones[i] = 0; val = i; while( val ) ++Ones[i], val &= (val - 1); } } int main() { //freopen("data.in", "r", stdin); getOnes(); while( scanf("%d %d %d", &N, &M, &K) == 3 ) { if( N == 0 && M == 0 && K == 0 ) break; init(); for(int i = 0; i < M; ++i) { scanf("%s", subString); insert(subString, i); } acmation(); for(int i = 0; i <= N; ++i) { for(int j = 0; j <= nE; ++j) { for(int k = 0; k < (0x1 << M); ++k) { dp[i][j][k] = 0; } } } dp[0][0][0] = 1; static int nC; static node *p; for(int i = 1; i <= N; ++i) { for(int j = 0; j <= nE; ++j) { for(int k = 0; k < (0x1 << M); ++k) { if( dp[i - 1][j][k] != 0 ) { for(nC = 0; nC < maxC; ++nC) { p = E[j].cld[nC]; dp[i][p->idx][k | p->status] += dp[i - 1][j][k]; dp[i][p->idx][k | p->status] %= MOD; } } } } } int ans = 0; for(int j = 0; j <= nE; ++j) { for(int k = 0; k < (0x1 << M); ++k) { if( Ones[k] < K ) continue; ans += dp[N][j][k]; ans %= MOD; } } printf("%d\n", ans); } return 0; }