zoukankan      html  css  js  c++  java
  • BZOJ1559 [JSOI2009]密码 【AC自动机 + 状压dp】

    题目链接

    BZOJ1559

    题解

    考虑到这是一个包含子串的问题,而且子串非常少,我们考虑(AC)自动机上的状压(dp)
    (f[i][j][s])表示长度为(i)的串,匹配到了(AC)自动机(j)号节点,且已匹配集合为(s)的方案数
    直接在(AC)自动机上转移即可
    但是为了防止使用(last)指针之类的,计算匹配的串,我们先将原串的集合去重和去包含关系

    方案怎么办?
    考虑到(ans le 42),一定是刚好若干个原串以最长前后缀相同的方式相接
    因为如果不是以最长前后缀的方式相接,那么一定存在更小的方案,这个时候就会多出若干空位置可以随便填
    一个位置可以填(26)种字母,而且可以放在前缀或者后缀,所以有一个空位置就有(52)种方案,大于(42)
    所以可以放心(O(n!))枚举排列相接

    (1A)了很开心

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<string>
    #include<cmath>
    #include<queue>
    #include<map>
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define mp(a,b) make_pair<int,int>(a,b)
    #define cls(s) memset(s,0,sizeof(s))
    #define cp pair<int,int>
    #define LL long long int
    using namespace std;
    const int maxn = 105,maxm = 100005,INF = 1000000000;
    inline int read(){
    	int out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    	return out * flag;
    }
    int ch[maxn][26],fail[maxn],tag[maxn],cnt,len[12];
    char s[12][12];
    int isin[maxn];
    int same[12][12];
    LL f[26][105][1 << 10];
    void ins(int p){
    	int u = 0,id;
    	for (int i = 1; i <= len[p]; i++){
    		id = s[p][i] - 'a';
    		u = ch[u][id] ? ch[u][id] : (ch[u][id] = ++cnt);
    	}
    	tag[u] |= (1 << p - 1);
    }
    void getf(){
    	queue<int> q;
    	for (int i = 0; i < 26; i++) if (ch[0][i]) q.push(ch[0][i]);
    	int u,v;
    	while (!q.empty()){
    		u = q.front(); q.pop();
    		for (int i = 0; i < 26; i++){
    			v = ch[u][i];
    			if (!v){
    				ch[u][i] = ch[fail[u]][i];
    				continue;
    			}
    			fail[v] = ch[fail[u]][i];
    			q.push(v);
    		}
    	}
    }
    int L,n;
    int check(int u,int v){
    	if (len[u] > len[v]) return 0;
    	if (len[u] == len[v]){
    		for (int i = 1; i <= len[u]; i++)
    			if (s[u][i] != s[v][i])
    				return 0;
    		return 1;
    	}
    	for (int i = 1; i <= len[v] - len[u] + 1; i++){
    		int flag = true;
    		for (int j = 1; j <= len[u]; j++){
    			if (s[u][j] != s[v][i + j - 1]){
    				flag = false;
    				break;
    			}
    		}
    		if (flag) return 2;
    	}
    	return 0;
    }
    int cal(int u,int v){
    	for (int i = max(1,len[u] - len[v] + 1); i <= len[u]; i++){
    		int flag = true;
    		for (int j = 1; i + j - 1 <= len[u]; j++)
    			if (s[u][i + j - 1] != s[v][j]){
    				flag = false;
    				break;
    			}
    		if (flag) return len[u] - i + 1;
    	}
    	return 0;
    }
    int vis[maxn],a[maxn],ansi,Ltot;
    struct node{
    	char s[30];
    	int len;
    }ans[50];
    inline bool operator <(const node& a,const node& b){
    	return strcmp(a.s + 1,b.s + 1) < 0;
    }
    void Check(){
    	int sum = Ltot;
    	for (int i = 1; i < n; i++)
    		sum -= same[a[i]][a[i + 1]];
    	if (sum == L){
    		ansi++;
    		for (int i = 1; i <= n; i++){
    			int u = a[i];
    			for (int j = same[a[i - 1]][u] + 1; j <= len[u]; j++){
    				ans[ansi].s[++ans[ansi].len] = s[u][j];
    			}
    		}
    	}
    }
    void dfs(int u){
    	if (u > n){
    		Check();
    		return;
    	}
    	for (int i = 1; i <= n; i++)
    		if (!vis[i]){
    			vis[i] = true;
    			a[u] = i;
    			dfs(u + 1);
    			vis[i] = false;
    		}
    }
    void work(){
    	for (int i = 1; i <= n; i++)
    		for (int j = 1; j <= n; j++)
    			if (i != j) same[i][j] = cal(i,j);
    	for (int i = 1; i <= n; i++) Ltot += len[i];
    	dfs(1);
    	sort(ans + 1,ans + 1 + ansi);
    	for (int i = 1; i <= ansi; i++)
    		printf("%s
    ",ans[i].s + 1);
    }
    int main(){
    	L = read(); n = read();
    	for (int i = 1; i <= n; i++){
    		scanf("%s",s[i] + 1);
    		len[i] = strlen(s[i] + 1);
    	}
    	for (int i = 1; i <= n; i++)
    		for (int j = 1; j <= n; j++)
    			if (i != j ){
    				int t = check(i,j);
    				if (t == 2 || (t == 1 && i > j))
    					isin[i] = true;
    			}
    	int tot = 0;
    	for (int i = 1; i <= n; i++)
    		if (!isin[i]){
    			strcpy(s[++tot] + 1,s[i] + 1);
    			len[tot] = len[i];
    			ins(tot);
    		}
    	n = tot;
    	getf();
    	int maxv = (1 << n) - 1,u;
    	f[0][0][0] = 1;
    	for (int i = 0; i < L; i++){
    		for (int j = 0; j <= cnt; j++){
    			for (int s = 0; s <= maxv; s++){
    				if (!f[i][j][s]) continue;
    				for (int k = 0; k < 26; k++){
    					u = ch[j][k];
    					f[i + 1][u][s | tag[u]] += f[i][j][s];
    				}
    			}
    		}
    	}
    	LL ans = 0;
    	for (int i = 0; i <= cnt; i++)
    		ans += f[L][i][maxv];
    	printf("%lld
    ",ans);
    	if (ans <= 42) work();
    	return 0;
    }
    
    
  • 相关阅读:
    数据库开发及ADO.NET
    C#典型案例及分析
    Visual Studio 2017 常用快捷键
    C#方法(函数)
    Linux常用命令----RPM包管理
    Linux常用命令----VIM命令
    Linux常用命令----压缩解压命令
    Linux常用命令----用户管理命令
    文件搜索命令
    Linux常用命令----权限管理命令
  • 原文地址:https://www.cnblogs.com/Mychael/p/9074401.html
Copyright © 2011-2022 走看看