Wireless Password [ 2009 Multi-University Training Contest 1 - Host by TJU ]

链接: 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;
}



你可能感兴趣的:(Wireless Password [ 2009 Multi-University Training Contest 1 - Host by TJU ])