【33.28%】【BZOJ 1195】[HNOI2006]最短母串
Time Limit: 10 Sec Memory Limit: 32 MB
Submit: 1208 Solved: 402
[Submit][Status][Discuss]
Description
给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T的子串。
Input
第一行是一个正整数n(n<=12),表示给定的字符串的个数。以下的n行,每行有一个全由大写字母组成的字符串。每个字符串的长度不超过50.
Output
只有一行,为找到的最短的字符串T。在保证最短的前提下,如果有多个字符串都满足要求,那么必须输出按字典序排列的第一个。
Sample Input
ABCD
BCDABC
Sample Output
【题解】
(看不懂的话就先看代码再看题解)
给出n个串。要找一个最短的串。满足n个所给的串都是这个串的子串。
设f[i][j]表示已经加入的字符串集合的状态为i,最后一个加入到该集合中的字符串的序号为j.
这里的加入集合过程。可以想象成把一个字符串拼接在另一个字符串后面。但是不同的是,这个拼接的过程有可能不会增加字符串的长度。即另一个字符串包括了这个字符串。右或者,拼接的时候长度不一定是两个字符串的长度之和。因为可能前一个字符串的后面有一部分和这个新加入的字符串的前面一样。
根据这个规则处理出cost[i][j]表示把j这个字符串"接到"i这个字符串后面会增加多少长度。
转移f[i][j]的时候。先枚举i这个状态的最后一个字符串是什么。
然后枚举哪一个字符串是这个i状态没有的。就尝试进行拼接(如果答案更优);
然后因为要字典序最小。在更新的过程中还要记录每个f[i][j]所代表的字符串是什么。不时地还要用strcmp进行比较。
然后是hash函数的部分。那个函数用了比价高的进制,以此来记录某段序列。第一眼看过去就能发现p[50]绝对会超unsigned int 的范围。但是超过之后是不会报错的。它会变小一点。然后超过了范围又变大。
可以观察一下p。
然后就是用加减来获取一个字符串里面某段区间的字符的hash值。
以此来判断是否有交集。或者说另一个串在前一个串的里面。
【代码】
- #include <cstdio>
- #include <cstring>
- unsigned int p[51];
- struct data2
- {
- char s[51];
- int len;
- int hash[51];
- unsigned int get_key(int begin, int end)//获取字符串里面[begin,end]区间的hash字符的hash值
- {
- return hash[end] - hash[begin - 1] * p[end - begin + 1];
- }
- };
- data2 a[13];
- int n,INF;
- int cost[13][13];
- int f[4096][13];
- char temp[601];
- char fa[4096][13][601];
- int min(int x, int y)
- {
- return x > y ? y : x;
- }
- int sear_ch(int x, int y)//判断x和y拼接在一起要增加多少长度
- {
- int len1 = a[x].len, len2 = a[y].len;
- int s = min(len1, len2);
- int ret = 0;
- bool flag = 0;
- if (len1 >= len2)
- flag = 1;
- if (flag)//这是字符串y为x的子串则不会增加强度
- {
- for (int i = 1; i <= len1 - len2 + 1; i++)
- {
- if (a[x].get_key(i, i + len2 - 1) == a[y].hash[len2])
- return -1;
- }
- }
- for (int i = 1;i <= s;i++)//如果不是子串就找交集的部分
- if (a[x].get_key(len1 - i + 1, len1) == a[y].hash[i])
- {
- ret = i;
- }
- return len2 - ret;
- }
- void input_data()
- {
- scanf("%d", &n);
- for (int i = 1; i <= n; i++)
- {
- scanf("%s", a[i].s+1);
- a[i].len = strlen(a[i].s + 1);
- for (int j = 1; j <= a[i].len; j++)//这个hash函数超级奇怪的。。。
- a[i].hash[j] = a[i].hash[j - 1] * 131 + a[i].s[j];
- }
- for (int i = 1;i <= n;i++)
- for (int j = 1; j <= n; j++)
- {
- if (i == j)
- continue;
- cost[i][j] = sear_ch(i, j);
- }
- }
- void init()
- {
- p[0] = 1;
- for (int i = 1; i <= 50; i++)
- p[i] = p[i - 1] * 131;
- }
- void get_ans()
- {
- memset(f, 127 / 3, sizeof(f));
- INF = f[0][0];
- for (int i = 1; i <= n; i++)
- {
- f[1 << (i - 1)][i] = a[i].len;//只有第i个字符串
- for (int j = 1; j <= a[i].len; j++)//则状态就是这个字符串
- fa[1 << (i - 1)][i][j] = a[i].s[j];
- fa[1 << (i - 1)][i][a[i].len + 1] = '\0';
- }
- for (int j = 0;j <= (1<<n)-1;j++)//从小到大枚举状态
- for (int i = 1;i <= n;i++)//枚举要拼到后面的字符串
- if (!(j&(1 << (i - 1))))//要求没有这个字符串才能拼接
- {
- for (int k = 1;k <= n;k++)//枚举j这个状态的最后一个字符串
- if (j &(1 << (k - 1)))//如果有这个字符串
- {
- if (cost[k][i] == -1)//是子串的情况
- {
- if (f[j][k] < f[j | (1 << (i - 1))][k])
- {
- f[j | (1 << (i - 1))][k] = f[j][k];
- for (int l = 1; l <= f[j][k]; l++)
- fa[j | (1 << (i - 1))][k][l] = fa[j][k][l];
- fa[j | (1 << (i - 1))][k][f[j][k] + 1] = '\0';
- }
- else
- if (f[j][k] == f[j | (1 << (i - 1))][k] && f[j][k] < INF)
- {
- for (int l = 1; l <= f[j][k]; l++)
- temp[l] = fa[j][k][l];
- temp[f[j][k] + 1] = '\0';
- if (strcmp(temp + 1, fa[j | (1 << (i - 1))][k] + 1) < 0)
- {
- for (int l = 1; l <= f[j][k]; l++)
- fa[j | (1 << (i - 1))][k][l] = temp[l];
- fa[j | (1 << (i - 1))][k][f[j][k] + 1] = '\0';
- }
- }
- }
- else
- {//可以更新的情况
- if (f[j][k] + cost[k][i] < f[j | (1 << (i - 1))][i])
- {
- f[j | (1 << (i - 1))][i] = f[j][k] + cost[k][i];
- for (int l = 1; l <= f[j][k]; l++)
- fa[j | (1 << (i - 1))][i][l] = fa[j][k][l];
- for (int l = 1; l <= cost[k][i]; l++)
- fa[j | (1 << (i - 1))][i][f[j][k] + l] = a[i].s[l + a[i].len - cost[k][i]];
- fa[j | (1 << (i - 1))][i][f[j][k] + cost[k][i] + 1] = '\0';
- }
- else
- if (f[j][k] + cost[k][i] == f[j | (1 << (i - 1))][i]
- && f[j | (1 << (i - 1))][i] < INF)
- {
- for (int l = 1; l <= f[j][k]; l++)
- temp[l] = fa[j][k][l];
- for (int l = 1; l <= cost[k][i]; l++)
- temp[f[j][k] + l] = a[i].s[l + a[i].len - cost[k][i]];
- temp[f[j][k] + cost[k][i] + 1] = '\0';
- if (strcmp(temp + 1, fa[j | (1 << (i - 1))][i] + 1) < 0)
- {
- for (int l = 1; l <= f[j][k] + cost[k][i]; l++)
- fa[j | (1 << (i - 1))][i][l] = temp[l];
- fa[j | (1 << (i - 1))][i][f[j][k] + cost[k][i] + 1] = '\0';
- }
- }
- }
- }
- }
- }
- void output_ans()
- {
- int mi = INF;
- for (int i = 1; i <= n; i++)
- mi = min(mi, f[(1 << n) - 1][i]);
- char ans[602];
- for (int i = 1; i <= mi; i++)//先让字典序最大。然后找最小的字典序
- ans[i] = 'z';
- ans[mi + 1] = '\0';
- for (int i = 1;i <= n;i++)
- if (f[(1 << n) - 1][i] == mi)
- {
- if (strcmp(fa[(1 << n) - 1][i] + 1, ans + 1)<0)
- strcpy(ans + 1, fa[(1 << n) - 1][i] + 1);
- }
- printf("%s\n", ans + 1);
- }
- int main()
- {
- //freopen("F:\\rush.txt", "r", stdin);
- init();
- input_data();
- get_ans();
- output_ans();
- return 0;
- }
【33.28%】【BZOJ 1195】[HNOI2006]最短母串的更多相关文章
- bzoj 1195: [HNOI2006]最短母串 爆搜
1195: [HNOI2006]最短母串 Time Limit: 10 Sec Memory Limit: 32 MBSubmit: 894 Solved: 288[Submit][Status] ...
- BZOJ 1195: [HNOI2006]最短母串
1195: [HNOI2006]最短母串 Time Limit: 10 Sec Memory Limit: 32 MBSubmit: 1346 Solved: 450[Submit][Status ...
- bzoj 1195 [HNOI2006]最短母串 bfs 状压 最短路 AC自动机
LINK:最短母串 求母串的问题.不适合SAM. 可以先简化问题 考虑给出的n个字符串不存在包含关系. 那么 那么存在的情况 只可能有 两个字符串拼接起来能表示另外一个字符串 或者某个字符串的后缀可以 ...
- 【刷题】BZOJ 1195 [HNOI2006]最短母串
Description 给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T的子串. Input 第一行是一个正整数n(n<=12) ...
- BZOJ 1195 [HNOI2006]最短母串 (Trie图+状压+bfs最短路)
BZOJ1195 LOJ10061 题目大意:给你$n$个模式串,求一个最短且字典序最小的文本串并输出这个串,$n<=12,len<=50$ 首先对所有模式串构造$Trie$图,$Trie ...
- bzoj 1195: [HNOI2006]最短母串【状压dp】
我有病吧--明明直接枚举是否匹配就可以非要写hash,然后果然冲突了(--我个非酋居然还敢用hash 设f[s][i]为已选串状态为s并且最后一个串是i,还有预处理出g[i][j]表示最长有长为g[i ...
- BZOJ 1195: [HNOI2006]最短母串 AC自动机+状压+搜索
思路比较直接. 由于 $n$ 很小,直接定义 $f[i][j]$ 表示当前在自动机中的节点 $i,$ 被覆盖串的集合为 $j$ 的方案数. #include <bits/stdc++.h> ...
- 【状态压缩dp】1195: [HNOI2006]最短母串
一个清晰的思路就是状压dp:不过也有AC自动机+BFS的做法 Description 给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T ...
- 1195: [HNOI2006]最短母串 - BZOJ
Description 给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T的子串.Input 第一行是一个正整数n(n<=12), ...
- 1195: [HNOI2006]最短母串
思路:好像以前谁问过我这题... 状个压就好啦, 把包含在其他串中的字符串删掉, 预处理除每两个字符串之间的关系, dp[ state ][ i ] 表示在state的状态下, 最后一个字符串是第i ...
随机推荐
- 微信公众号系统在Linux下的部署操作
ps -ef | grep tomcat 查看tomcat进程
- 《第一行代码》之——1.Android简介
Android简介 Android系统架构 (图片源自维基百科) Android大致分为四层架构,五块区域. Linux内核层 Android系统基于Linux2.6,这一层为Android设备的各种 ...
- hdu2018 dp
/* 1~4直接取得: 然后后面的生牛的时候都是前一年的加上一定的数. 从第5年看,第五年出生的牛肯定要加上第四年出生的,然后由于第一个出生的牛开始生小牛,这和 最开始的牛生孩子是一样的,所以+dp[ ...
- Effective C++: 03资源管理
所谓资源,就是一旦用了它,将来必须还给系统.C++中的资源有:内存.文件描述符.互斥锁.数据库连接.网络socket等. 13:以对象管理资源 1:像下面这个函数: void f() { Invest ...
- Oracle使用——PLSQL查询表结构并导出EXCEL
背景 有一次需要查询Oracle数据库中的所有表接口并且导出excel,方法记录如下 使用 使用PLSQL工具查询表结构,SQL语句如下 SELECT B.TABLE_NAME AS '表名', C. ...
- python while 循环结构
- 选择阿里云数据库HBase版十大理由
根据Gartner的预计,全球非关系型数据库(NoSQL)在2020~2022预计保持在30%左右高速增长,远高于数据库整体市场. 阿里云数据库HBase版也是踏着技术发展的节奏,伴随着NoSQL和大 ...
- 3331: [BeiJing2013]压力
3331: [BeiJing2013]压力 LCA+树上差分,和之前类似的题差不多,就是多了个v-dcc缩点,唯一要注意的就是判断是否是割点,对于不是割点的点,如果他是起点或重点,ans++,和差分没 ...
- 学习canvas画布
我们可以用画布(Canvas)绘制各种图形,下面代码是绘制的一个圆形: <!DOCTYPE html> <html> <head> <title>Can ...
- hdu 1789 Doing Homework again (Greedy)
Problem - 1789 继续贪心.经典贪心算法,如果数据比较大就要用线段树来维护了. 思路很简单,只要按照代价由大到小排序,然后靠后插入即可.RE了一次,是没想到deadline可以很大.如果d ...