2019暑假集训 文件压缩

题目背景

提高文件的压缩率一直是人们追求的目标。近几年有人提出了这样一种算法,它虽然只是单纯地对文件进行重排,本身并不压缩文件,但是经这种算法调整后的文件在大多数情况下都能获得比原来更大的压缩率。

题目描述

该算法具体如下:对一个长度为nn的字符串SS,首先根据它构造nn个字符串,其中第ii个字符串由将SS的前i-1i−1个字符置于末尾得到。然后把这nn个字符串按照首字符从小到大排序,如果两个字符串的首字符相等,则按照它们在SS中的位置从小到大排序。排序后的字符串的尾字符可以组成一个新的字符串SS’,它的长度也是nn,并且包含了SS中的每一个字符。最后输出SS’以及SS的首字符在SS’中的位置pp。举例:

S:example

1、构造nn个字符串

example

xamplee

ampleex

mpleexa

pleexam

leexamp

eexampl

2、将字符串排序

ampleex

example

eexampl

leexamp

mpleexa

pleexam

xamplee

3、压缩结果

xelpamexelpame SS’

77 pp

由于英语单词构造的特殊性,某些字母对出现的频率很高,因此在SS’中相同的字母有很大几率排在一起,从而提高SS’的压缩率。虽然这种算法利用了英语单词的特性,然而在实践的过程中,人们发现它几乎适用于所有的文件压缩。

请你编一个程序,读入SS’和pp,输出字符串SS。

输入格式

共三行。

第一行是一个整数n(1 \le n \le 10000)n(1≤n≤10000),代表SS’的长度。

第二行是字符串SS’。

第三行是整数pp。

输出格式

一行,S。

输入输出样例

输入 #1复制

7
xelpame
7

输出 #1复制

example题目来源洛谷P1124

一眼就能看出是一个模拟首先我们明确两个规律:(1)字典序排序后的每个字符串首字母在原串中的位置一定等于尾字母在原串中的位置+1(2)字典序排序后首字母相同的字符串,越排在后面的尾字母在原串中的位置越靠前(原串中第一个、最后一个字母需要特殊讨论)又因为给定的S‘是所有字符串取右边得到的新字符串,它包含了原串中每一个字母,又因为是按照字典序排序,所有我们将S‘按照字典序排序竖着写在左边即可得到排序后每个串首位字母的对应关系用样例举例:S‘为xelpame则排序后的第一个字母依次为aeelmpx(字典序)也就是说,这些串依次为:a……xe……ee……ll……pm……ap……mx……e我们将aeelmpx称为左部,xelpame称为右部,分别用l,r两个字符串表示又因为我们知道原串首字母在右部字符串中的位置p,(即原串首字母为排序后第p个字符串的尾字母),设首字母为r[p]根据规律(2),我们容易知道在左部字符串中所有以r[p]为首字母的字符串当中,最靠上面的一个肯定是原串(因为不可能有另一个串比它在原串中位置靠前)所以我们容易找到原串在排序后所有字符串中的位置idx但我们不知道这个完整的串,我们只知道这个串最后一个字母是什么(即r[idx]),而r[idx]一定是原串的尾字母,所以我们用ans记录原串,则ans[len]=r[idx]根据规律(1),我们可以知道,排序后字符串以原串尾字母开头的那一个的尾字母一定是原串的倒数第二个字母,但由于可能有很多个与原串尾字母同样的字母,我们需要找到以该字母开头的字符串中最靠下的一个。其原因是按照在原串中的位置排序,没有另一个串可以比倒着依次找原串字母更靠后(因为所有比它靠后的串一定被找过)换句话说,规律(2)告诉我们,首字母相同的字符串,尾字母在原串中靠前的一定整个串排的靠前(最靠前的是原串不算),但由于我们是倒着找答案,所以我们应该找最靠后的没有找过的字符串(这个没有找过值80分),这样找到的尾字母才能逐渐从后往前构成答案。上代码
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
char r[10050],l[10050],ans[10050],book[10050];
int len,p;
bool cmp(char a,char b)
{
    return a<b;
}//排序左部字符串
int main()
{
    scanf("%d",&len);
    for(int i=1;i<=len;i++)cin>>r[i];
    scanf("%d",&p);
    for(int i=1;i<=len;i++)l[i]=r[i];
    sort(l+1,l+len+1,cmp);
    int idx;
    for(int i=1;i<=len;i++)
        if(l[i]==r[p])
        {
            idx=i;
            break;
        }//从前往后找到左部中原串首字母所在位置
    int temp=len;
    while(temp>=1)//倒着生成原串
    {
        ans[temp--]=r[idx];
        for(int i=len;i>=1;i--)//倒着找左部字符串中的r[idx]
            if(l[i]==r[idx]&&!book[i])
            {
                book[i]=1;//注意标记这个串已经找过
                idx=i;
                break;
            }
    }
    for(int i=1;i<=len;i++)putchar(ans[i]);//正序输出
    return 0;
}

原文地址:https://www.cnblogs.com/qxds/p/11376336.html

时间: 2024-07-30 16:16:33

2019暑假集训 文件压缩的相关文章

「总结」2019暑假集训

啊,我最喜欢的暑假集训终究还是结束了. 感觉集训收获的还是挺大的,不管是在知识方面还是心态方面,感觉现在考试心态稳了很多,不管是考前考时考后,都可以很快的调整了.大概就是教练所说的考试心态调整的加速.最近感觉非常好,虽然水题还是老爆零,考得也不怎么样,不过我的确是飞快的在进步了,只要我在进步就好了,我很心满意足的. 总结一下接近30场的考试吧. 一开始的7场一直只有50分左右,而且还有两次没有交卷子,气得我把纸贴在电脑上提醒自己交卷子,虽然成绩并不怎么样,不过我还好在也不犯这个错误了. 然后就理

2019暑假集训DAY1(problem3.play)(单调栈思想)

题面 play 题目大意 这个位面存在编号为1~2N的2N个斗士,他们正为争夺斗士大餐展开R轮PVP,每个斗士i都有一个固有APM ai,和一个初始斗士大餐储量 bi.每轮开始前以及最后一轮结束之后,2N个斗士会重新按照各自斗士大餐的储量进行排序(斗士大餐储量相同时编号小的靠前),每轮中,第1名和第2名PVP,第3名和第4名PVP,……第2k-1名和第2k名PVP,第2N-1名和第2N名PVP.而每场一对一的PVP都非常无聊,总是两个斗士中APM高的获胜,另一方失败:或者APM相同的两方取得平手

2019暑假集训 Intervals

题目描述 给定n个闭区间[ai,bi]和n个整数ci.你需要构造一个整数集合Z,使得对于任意i,Z中满足ai<=x<=bi的x不少于ci个.求Z集合中包含的元素个数的最小值. 输入 第一行为一个整数n(1<=n<=50000) 接下来n行每行描述一个区间,三个整数分别表示ai,bi和ci.( 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1.) 输出 输出一个整数,表示Z中包含元素个数的最小值. 样例输入 5

2019暑假集训 8/2

学习内容:线段树+可持久化线段树 今日完成题数(不包含多校):5 /*多校补题情况(之前定的每支队伍标准):?*/ 今日看书情况:3页 学习算法的总结 可持久化线段树一直没有好好研究 直到最近着重开始写线段树专题 写了一些权值线段树 才把之前有的疑问的解决了 今日做题总结    hdu-6183 https://www.cnblogs.com/MengX/p/11291321.html hdu-4630 https://www.cnblogs.com/MengX/p/11291349.html

2019暑假集训 数字游戏

题目描述 科协里最近很流行数字游戏.某人命名了一种不降数,这种数字必须满足从左到右各位数字成小于等于的关系,如123,446.现在大家决定玩一个游戏,指定一个整数闭区间[a,b],问这个区间内有多少个不降数. 输入 有多组测试数据.每组只含两个数字a,b,意义如题目描述. 输出 每行给出一个测试数据的答案,即[a,b]之间有多少不降数. 样例输入 1 9 1 19 样例输出 9 18 提示 对于全部数据,1<=a<=b<=2^31-1. 裸的数位dp,每一位从上一位开始搜即可(保证不下降

2019暑假集训 8/16

学习内容:网络流 今日完成题数(不包含多校): 今日看书情况:无 学习算法的总结 有上下界的网络流 和 最小费用可行流 今日做题总结 总结 今日心得: https://blog.csdn.net/forever_dreams/article/details/81879277 https://blog.csdn.net/qq_43202683/article/details/90049597 明日任务:   明天写完基础的可行流题目 就可以写一写gym的进阶题 原文地址:https://www.c

2019暑假集训8.22(problem2.dinner)(二分)

因为求最大时间的最小,考虑到二分答案. 常规思路:断环为链,二倍链. 最暴力的做法是在n个位置都断一次环,序列for一遍暴力分组,大于mid了就分到下一组,时间O(n^2 logn) 考虑优化: 我们发现每一次暴力分组是把整个序列都给走了一遍,分好的组满足和<=mid,与其一个个的把值加到和里,不如先处理出前缀和,然后对于每一个组的起点,二分找出这个组的终点(我代码中是用upper_bound实现的) int s=t[q],c=1;//s即为起点 int flagg=0; for(int i=q

2019暑假集训8.24(problem2.baritone)(链表(巧妙数据结构))

数据结构好题! 因为思路是第一次见,所以就直接说思路. 题目抽象: 这是一个矩形 里面有很多的点,求至少覆盖k个点的矩形有多少个 先确定上边界,下边界为低端 上边界下面的点用链表存起来 考虑以每个点作为左边界的贡献(线上的点也算在矩形内),假如k=3,那么右边界至少在橙色这根线这儿 符合要求的矩形的左边界范围L和右边界范围R如图 做出的贡献为L*R,如果前面还有点注意是左开右闭(因为右边到底都是可以的) 然后移动左边界 到下一个点再计算贡献 再来移动下边界 一些点可能在同一水平线上,所以一开始可

2019暑假总结7.15-8.23+大一总结

2019暑假集训7.15-8.23 今年大一下学期整个暑假基本上是了解学习了dp.动态规划.线段树.树状数组.贪心.搜索.背包问题.记忆化搜索.欧拉回路.最大流.最短路.最小生成树.二叉搜索树.kmp.数论..马拉车等,感觉每天在敲代码,可是整体看下来学的东西很少,但是谢谢题目又是好久才能写出一道题. 等到明天九月二号一来到,我就是大二的学生了,大二这整一年是最关键的一年,一定要加油加油. 原文地址:https://www.cnblogs.com/OFSHK/p/11444186.html