UVa 1076 Password Suspects (AC自动机+状压dp)

题目链接:https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3517

生成文本,要求含或不含某些连续子串的题目一般是 (AC) 自动机 (+) (dp) 的套路

(dp[u][len][st]) 表示当前在状态图的节点 (u),当前生成的文本长度为 (len),含子串的状态为 (st) 时的方案数

注意: 因为一个单词节点可能是多个单词的结尾((last) 函数的含义),所以在计算失配函数 (fail) 指针时要同时更新当前节点的 (last)(val)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 405;

int n, m; 
int ok;
char s[20];

int ch[maxn][30], f[maxn], val[maxn], last[maxn];
ll dp[maxn][27][1<<11];

int sz = 0, rt = 0;

void init(){
	sz = 0;
	ok = (1<<m)-1;
	memset(ch, 0, sizeof(ch));
	memset(val, 0, sizeof(val));
	memset(f, 0, sizeof(f));
	memset(last, 0, sizeof(last));
	memset(dp, -1, sizeof(dp));
}

int idx(char c){
	return c - 'a';
}

void insert(int num){
	int p = rt, len = strlen(s);
	for(int i = 0 ; i < len ; ++i){
		int c = idx(s[i]);
		if(!ch[p][c]) {
			++sz;
			val[sz] = 0;
			ch[p][c] = sz;
		}
		p = ch[p][c];
	}
	val[p] |= (1<<num);
}

void getfail(){
	queue<int> q;
	f[0] = 0;
	
	for(int i = 0 ; i < 26 ; ++i){
		int u = ch[0][i];
		if(u) {
			f[u] = 0; q.push(u); last[u] = 0;
		}
	}
	
	while(!q.empty()){
		int r = q.front(); q.pop();
		for(int i = 0 ; i < 26 ; ++i){
			int u = ch[r][i];
			if(!u) {
				ch[r][i] = ch[f[r]][i];
				continue;
			}
			q.push(u);
			int v = f[r];
			while(v && !ch[v][i]) v = f[v];
			f[u] = ch[v][i];
			last[u] = val[f[u]] ? f[u] : last[f[u]];
			val[u] |= val[last[u]]; //***
		}
	}
}

ll dfs(int u, int len, int st){
	if(dp[u][len][st] != -1) return dp[u][len][st];
	if(len == n) return dp[u][len][st] = (st == ok);
	
	dp[u][len][st] = 0;
	for(int i = 0 ; i < 26 ; ++i){
		dp[u][len][st] += dfs(ch[u][i], len+1, st|val[ch[u][i]]);
	}
	return dp[u][len][st];
}

char ss[110];
int l = 0;

void print(int u, int len, int st){
	if(len == n){
		printf("%s
", ss+1);
		return;
	}
	
	for(int i = 0 ; i < 26 ; ++i){
		if(dp[ch[u][i]][len+1][st|val[ch[u][i]]]){
			ss[len+1] = i+'a';
			print(ch[u][i], len+1, st|val[ch[u][i]]);
		}
	}
}

ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }

int main(){
//	freopen("ans.out", "w", stdout);
	int kase = 0;
	while(scanf("%d%d", &n, &m) == 2 && n){
		memset(ss, 0, sizeof(ss));
		init();
		
		for(int i = 0 ; i < m ; ++i){
			scanf("%s", s);
			insert(i);
		}
		
		getfail();
		
		ll ans = dfs(0, 0, 0);
		printf("Case %d: ", ++kase);
		printf("%lld suspects
", ans);
		
		if(ans <= 42){
			print(0, 0, 0);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/tuchen/p/15230621.html