题目

给定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\n",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] = '\0';
printf("%s\n",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;
}

BZOJ1195 [HNOI2006]最短母串 【状压dp】的更多相关文章

  1. [bzoj1195][HNOI2006]最短母串_动态规划_状压dp

    最短母串 bzoj-1195 HNOI-2006 题目大意:给一个包含n个字符串的字符集,求一个字典序最小的字符串使得字符集中所有的串都是该串的子串. 注释:$1\le n\le 12$,$1\le ...

  2. Bzoj1195 [HNOI2006]最短母串 [状态压缩]

    Time Limit: 10 Sec  Memory Limit: 32 MBSubmit: 1304  Solved: 439 Description 给定n个字符串(S1,S2,„,Sn),要求找 ...

  3. [bzoj1195] [hnoi2006] 最短母串

    本题是一个经典的状压dp问题,在紫书中有着加强版的例题. 本题的难度主要体现在:如何输出字符串字典序最小. 为了解决这个问题,我们有两种常用方案: 1) 我们可以采用bfs输出路径的方法,使用+1来输 ...

  4. BZOJ1195[HNOI2006]最短母串——AC自动机+BFS+状态压缩

    题目描述 给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T的子串. 输入 第一行是一个正整数n(n<=12),表示给定的字符串的 ...

  5. Bzoj1195 [HNOI2006]最短母串 [AC自动机]

    Time Limit: 10 Sec  Memory Limit: 32 MBSubmit: 1304  Solved: 439 Description 给定n个字符串(S1,S2,„,Sn),要求找 ...

  6. BZOJ1195 HNOI2006最短母串(状压dp)

    按照子串出现的先后考虑.令f[i][j]为已经出现的字符串集合为i,最后一个出现的字符串为j时的最短串长,预处理一下任意两个串的最长重叠长度,转移显然.有点麻烦的是字典序,强行增加代码难度. 另一个比 ...

  7. bzoj1195 [HNOI2006]最短母串 AC 自动机+状压+bfs

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=1195 题解 建立 AC 自动机,然后构建出 trie 图. 然后直接在 trie 图上走.但是 ...

  8. BZOJ1195 [HNOI2006]最短母串 AC自动机 bfs

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 传送门 - BZOJ1195 题意概括 给出一堆串,然后求一个包含这些串的所有串的最短的中的字典序最小的. 题解 先造一个AC ...

  9. [BZOJ1195]:[HNOI2006]最短母串(AC自动机+BFS)

    题目传送门 题目描述 给定n个字符串(S1,S2,…,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,…,Sn)都是T的子串. 输入格式 第一行是一个正整数n,表示给定的字符串的个数 ...

随机推荐

  1. CodeForces 48C D - The Race (Fraction,数学)

    每个加油的站可以确定一个alpha的上下界,比如,第i次加油站a[i],前面加了i次油,a[i]*10≤ alpha*i <(a[i]+1)*10. 取最大的下界,取最小的上界,看看两者之间的满 ...

  2. CF Gym 100637J Superfactorial numeral system (构造)

    题意:给一个式子,ak,k>2时,0<=ak<k:ai都是整数,给你p,q让你求一组ak. 题解:构造,每次除掉q取整得到ai,然后减一减 #include<cstdio> ...

  3. SpringMVC归纳

    SpringMVC归纳 操作流程 配置前端控制器 在web.xml中配置 配置处理器映射器 在springmvc配置文件中配置 配置处理器适配器 在springmvc配置文件中配置 配置注解适配器和映 ...

  4. 用python写trojan的过程中遇到的各种问题

    由于之前已经conn, addr = s.accept() 所以改为  conn.recv spyder无法同时运行client 和 server 分别在spyder和anaconda prompt运 ...

  5. Python3之偏函数

    通过设定参数的默认值,可以降低函数调用的难度.偏函数可以做到这一点 int()函数可以把字符串转换成十进制整数,当传入字符串时,int()默认把字符串为十进制 >>> int('12 ...

  6. 【vue iview】项目 win10 放在C盘 经常npm install不成功,就是因为 权限问题,把代码目录放到D盘就没事了。

    [vue iview]项目 win10 放在C盘 经常npm install不成功,就是因为 权限问题,把代码目录放到D盘就没事了.

  7. HTML之基本语法(链接标签、路径的介绍和使用)

    一.链接标签 语法:<a href="目标地址">这个标签上展示的内容</a> 作用:可以实现在当前页面跳转到新页面的操作 属性 1.target这个属性可 ...

  8. Acronis.Disk.Director磁盘分区管理

    Acronis.Disk.Director分为for 专业版和服务器版的,我在生产环境中调整Windows2003跳板机使用的是Acronis.Disk.Director Server 10.0.20 ...

  9. 【转】树莓派3代3.5寸触摸屏驱动的安装(通过ssh安装)

    这是用到的配件的树莓派3代 烧录好系统后,启动的树莓派,我的树莓派已经在一开始通过路由器和局域网,登陆了ssh,设置好了开机就能自动连接到电脑的360wifi,所以无论到哪 里,只要自己的笔记本电脑还 ...

  10. Python-DDT实现接口自动化

    Get请求参数化例子 import unittest import requests import ddt @ddt.ddt class MyTestCase(unittest.TestCase): ...