[日常训练] 压缩
来源:互联网 发布:thinkphp分销系统源码 编辑:程序博客网 时间:2024/06/08 19:36
PS:由于编辑器原因以下用“#”代替“$”
【题目描述】
巨大的文本往往令人头疼,特别是文本内容有大量重复的情况下,巨大的文本不便于运输和阅读,于是我们提出了NOIP(Nonsense Obivous Index Pattern(荒谬的显然索引法)),一种“有效的”压缩文本的方法。
NOIP压缩后的格式很特别,一个文本压缩后由若干个单元组成,每个单元由3部分组成:1.正文(小写字母组成的字符串)2.若干个“*”,表示正文的又重复了几次3.单元的结尾符号“#”
比如,NOIP压缩后的文本hello**#yes#no****#的含义是“hello”重复3次,yes重复1次,no重复5次,解压后就变成hellohellohelloyesnonononono。
显然,对于同一文本,压缩后的表示方法不唯一,但是为了方便,我们要求你采用压缩后字符串最短的压缩方法,如果有多种压缩方法,只需输出任意一种。(special judge)
【输入格式】
一个字符串,只含小写字母,表示原来的文本。
【输出格式】
一个字符串,表示一种最短的压缩后文本。(special judge)
【输入样例1】
aaaa
【输出样例1】
aa*#
【样例解释1】
除此外还有多种压缩方法,但是长度都比样例输出长,以下列举其中几种:
a***#
aaaa#
a**#a#
【样例输入2】
hellohellohelloyesnonononono
【样例输出2】
hello**#yes#no****#
【数据范围】
记len为读入的字符串长度。
20%的数据,len<=10
70%的数据,len<=200
100%的数据,len<=2000
【分析】字符串哈希/KMP + DP
- 记
f[i] 表示处理到第i 位时当前最短的压缩文本长度,则转移显然为f[i]=f[j−1]+len[j][i] (其中len[j][i] 表示从第j 位到i 位最短的压缩文本长度) - 考虑到时间复杂度,我们需要预处理出
len[j][i] ,可用字符串哈希或KMP 解决 - 但最后不是输出最短长度而是压缩后的文本,我们记录每次转移到
i 的最优决策j−1 ,并在预处理len[j][i] 时同时预处理出第j 位到第i 位压缩文本最短时循环节的长度和个数,最后沿着最优决策倒着找回去,也就解决了输出的问题
【代码】
#include <iostream>#include <cstdio>#include <cstring>using namespace std;typedef unsigned long long ull;const int Maxn = 0x3f3f3f3f;const int N = 2005;ull p[N], h[N]; char s[N]; int len[N][N], apr[N][N], str[N][N], ret[N], stk[N], f[N];int tmp, top, n;inline ull Hash(const int &x, const int &y){ return h[y] - h[x - 1] * p[y - x + 1];}int main(){ freopen("compress.in", "r", stdin); freopen("compress.out", "w", stdout); memset(len, Maxn, sizeof(len)); memset(f, Maxn, sizeof(f)); p[0] = 1; scanf("%s", s + 1); n = strlen(s + 1); for (int i = 1; i <= n; ++i) p[i] = p[i - 1] * 26; for (int i = 1; i <= n; ++i) h[i] = h[i - 1] * 26 + s[i] - 'a'; for (int k = 1; k <= n; ++k) for (int i = 1; i <= n; ++i) for (int j = i; j + k - 1 <= n; j += k) { if (j > i && Hash(j - k, j - 1) != Hash(j, j + k - 1)) break; tmp = k + (j - i) / k + 1; int ed = j + k - 1; if (len[i][ed] > tmp) { len[i][ed] = tmp; str[i][ed] = k; apr[i][ed] = (j - i) / k; // str[i][ed],apr[i][ed]分别表示 // 当压缩文本最短时,第i位到第ed位循环节的长度和个数 } } f[0] = 0; f[1] = 2; for (int i = 2; i <= n; ++i) for (int j = 1; j <= i; ++j) { tmp = f[j - 1] + len[j][i]; if (f[i] > tmp) f[i] = tmp, ret[i] = j - 1; } for (int i = n; i; i = ret[i]) stk[++top] = i; // stk[top…1]表示按位置顺序的最优决策 tmp = 1; for (int i = top; i >= 1; --i) { for (int j = tmp; j <= tmp + str[tmp][stk[i]] - 1; ++j) putchar(s[j]); for (int j = 1; j <= apr[tmp][stk[i]]; ++j) putchar('*'); putchar('$'); tmp = stk[i] + 1; } fclose(stdin); fclose(stdout); return 0;}
阅读全文
1 0
- [日常训练] 压缩
- HEU日常训练10.02
- 日常训练小结
- 日常训练20161012 道路网
- 日常训练20161012 醉酒
- 日常训练20161014 跟踪
- 日常训练20161018 证据
- 日常训练20161018 subset
- 日常训练 平均数
- 日常训练 水箱
- 日常训练 棋盘游走
- 日常训练 20170531 数字
- 日常训练 20170531 探险
- 日常训练 20170531 矩阵
- 日常训练 20170602 Book
- 日常训练 20170602 Equation
- 日常训练 20170603 棋盘
- 日常训练 20170605 EasyProblem
- MDX同比环比的计算
- css瀑布流三种写法的区别
- linux系统升级python到2.7.13
- 求二叉树叶子节点的个数+求二叉树第k层的节点个数
- jquery源码解析(第3章元素之坐标算法)
- [日常训练] 压缩
- 出差记录
- Just a Hook HDU
- Linux下jenkins改端口、解决内存溢出、版本升级
- OpenStack新组件打包
- 数据结构从零单排1——顺序表
- E
- JavaScript严格模式约束规则
- C++中string类下的begin,end,rbegin,rend的用法