zoukankan      html  css  js  c++  java
  • BZOJ1195 [HNOI2006]最短母串 【状压dp】

    题目

    给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T的子串。

    输入格式

    第一行是一个正整数n(n<=12),表示给定的字符串的个数。
    以下的n行,每行有一个全由大写字母组成的字符串。每个字符串的长度不超过50.

    输出格式

    只有一行,为找到的最短的字符串T。在保证最短的前提下,
    如果有多个字符串都满足要求,那么必须输出按字典序排列的第一个。

    输入样例

    2

    ABCD

    BCDABC

    输出样例

    ABCDABC

    题解

    写完状压dp后才知道可以用AC自动机水,而且十分简洁。。。

    我还是说状压dp吧。。
    思想简单却十分难写,,,

    我们先将其它串的子串去掉,保证两两完全不包含
    (f[s][i])表示当前串集合为(s),最后一个为(i)号串的最小长度,同时记录一个(pre)数组记录状态转移的方向
    再预处理一个(at[i][j])表示在(i)串后接(j)串增加的长度

    转移十分显然,枚举下一个不在集合中的串,如果方案更优则转移,如果同样优,利用pre数组还原串比较字典序

    所有的字符串比较之类的都可以直接暴力

    虽然代码长了一点丑了一点,但是跑得飞快

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define ULL unsigned long long int
    #define LL long long int
    #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 BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
    #define mp(a,b) make_pair<int,int>(a,b)
    #define cp pair<int,int>
    using namespace std;
    const int maxn = 15,maxm = (1 << 13),maxl = 55,INF = 1000000000;
    struct String{
    	char s[maxl];
    	int len;
    	ULL h[maxl];
    }S[maxn];
    cp pre[maxm][maxn];
    int n,at[maxn][maxn],f[maxm][maxn];
    ULL P[1000];
    void exclude(){
    	for (int i = 1; i <= n; i++){
    		for (int j = 1; j <= n; j++){
    			if (j != i && S[j].len >= S[i].len){
    				int flag = false,L = S[j].len - S[i].len + 1;
    				for (int k = 1; k <= L; k++){
    					if (S[i].h[S[i].len] == S[j].h[k + S[i].len - 1] - S[j].h[k - 1] * P[S[i].len]){
    						flag = true;
    						break;
    					}
    				}
    				if (flag){
    					swap(S[i--],S[n--]);
    					break;
    				}
    			}
    		}
    	}
    	//for (int i = 1; i <= n; i++)
    	//	printf("%s
    ",S[i].s + 1);
    }
    void init(){
    	REP(i,n) REP(j,n) if (i != j){
    		for (int k = max(1,S[i].len - S[j].len + 1); k <= S[i].len; k++){
    			int L = S[i].len - k + 1;
    			if (S[j].h[L] == S[i].h[S[i].len] - S[i].h[k - 1] * P[L]){
    				at[i][j] = L;
    				break;
    			}
    		}
    	}
    	//REP(i,n) printf("%d ",at[1][i]); puts("");
    }
    char t1[1000],t2[1000];
    int s1[55],s2[55],top1,top2,n1,n2;
    int cmp(int a,int b,int c,int d,int x){
    	top1 = 0;
    	while (a && b){
    		s1[++top1] = b;
    		cp tmp = pre[a][b];
    		a = tmp.first;
    		b = tmp.second;
    	}
    	n1 = 0;
    	for (int i = top1,last = 1; i; i--){
    		int u = s1[i];
    		for (int j = last; j <= S[u].len; j++){
    			t1[++n1] = S[u].s[j];
    		}
    		last = at[u][s1[i - 1]] + 1;
    	}
    	if (x){
    		int l = at[s1[1]][x] + 1;
    		for (int i = l; i <= S[x].len; i++)
    			t1[++n1] = S[x].s[i];
    	}
    	top2 = 0;
    	while (c && d){
    		s2[++top2] = d;
    		cp tmp = pre[c][d];
    		c = tmp.first;
    		d = tmp.second;
    	}
    	n2 = 0;
    	for (int i = top2,last = 1; i; i--){
    		int u = s2[i];
    		for (int j = last; j <= S[u].len; j++){
    			t2[++n2] = S[u].s[j];
    		}
    		last = at[u][s2[i - 1]] + 1;
    	}
    	if (x){
    		int l = at[s2[1]][x] + 1;
    		for (int i = l; i <= S[x].len; i++)
    			t1[++n2] = S[x].s[i];
    	}
    	for (int i = 1; i <= n1; i++)
    		if (t1[i] != t2[i]) return t2[i] - t1[i];
    	return 0;
    }
    void print(int x){
    	int a = (1 << n) - 1,b = x;
    	top1 = 0;
    	//puts("");
    	while (a && b){
    		//puts(S[b].s + 1);
    		s1[++top1] = b;
    		cp tmp = pre[a][b];
    		a = tmp.first;
    		b = tmp.second;
    	}
    	n1 = 0;
    	for (int i = top1,last = 1; i; i--){
    		int u = s1[i];
    		for (int j = last; j <= S[u].len; j++){
    			t1[++n1] = S[u].s[j];
    		}
    		last = at[u][s1[i - 1]] + 1;
    	}
    	t1[n1 + 1] = '';
    	printf("%s
    ",t1 + 1);
    }
    void solve(){
    	fill(f[0],f[0] + maxm * maxn,INF);
    	int maxv = (1 << n) - 1;
    	for (int i = 1; i <= n; i++) f[1 << i - 1][i] = S[i].len;
    	for (int s = 0; s <= maxv; s++){
    		for (int i = 1; i <= n; i++){
    			if (!(s & (1 << i - 1))) continue;
    			for (int j = 1; j <= n; j++){
    				if ((s | (1 << j - 1)) != s){
    					int e = (s | (1 << j - 1)),len = S[j].len - at[i][j];
    					if (f[e][j] > f[s][i] + len){
    						f[e][j] = f[s][i] + len;
    						pre[e][j] = mp(s,i);
    					}
    					else if (f[e][j] == f[s][i] + len){
    						int tmp = cmp(s,i,pre[e][j].first,pre[e][j].second,j);
    						if (tmp > 0) pre[e][j] = mp(s,i);
    					}
    				}
    			}
    		}
    	}
    	int ans = 1;
    	for (int i = 2; i <= n; i++){
    		if (f[maxv][i] < f[maxv][ans] || (f[maxv][i] == f[maxv][ans] && cmp(maxv,i,maxv,ans,0) > 0)){
    			ans = i;
    		}
    	}
    	print(ans);
    }
    int main(){
    	P[0] = 1;
    	for (int i = 1; i < 1000; i++) P[i] = P[i - 1] * 107;
    	scanf("%d",&n);
    	for (int i = 1; i <= n; i++){
    		scanf("%s",S[i].s + 1);
    		S[i].len = strlen(S[i].s + 1);
    		for (int j = 1; j <= S[i].len; j++){
    			S[i].h[j] = S[i].h[j - 1] * 107 + S[i].s[j] - 'A';
    		}
    	}
    	exclude();
    	init();
    	solve();
    	return 0;
    }
    
    
  • 相关阅读:
    幂等性
    接口开发需要考虑内容
    接口安全保护策略
    高并发之API接口限流
    Windows 下通过DOS命令获取指定文件夹下所有文件的全路径
    spring cloud 版本号与 boot版本之间的对应关系(版本不对,会导致pom无法引入)
    springCloud全家桶
    map的循环删除操作
    Java hashCode() 和 equals()的若干问题解答
    SpringMVC工作流程
  • 原文地址:https://www.cnblogs.com/Mychael/p/8795260.html
Copyright © 2011-2022 走看看