• 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;
    }
    
    
  • 相关阅读:
    Maven 环境的配置
    zTree的简单例子
    plsql免安装客户端的配置
    HDU 1232 畅通工程
    HDU 5698 瞬间移动
    Codeforces 1015E1 Stars Drawing (Easy Edition)
    Codeforces 784B Santa Claus and Keyboard Check
    Codeforces 500C New Year Book Reading
    NSarray 赋值 拷贝 等问题记录
    UINavigationController 操作记录
  • 原文地址:https://www.cnblogs.com/Mychael/p/8795260.html
Copyright © 2020-2023  润新知