题目背景
提高文件的压缩率一直是人们追求的目标。近几年有人提出了这样一种算法,它虽然只是单纯地对文件进行重排,本身并不压缩文件,但是经这种算法调整后的文件在大多数情况下都能获得比原来更大的压缩率。
题目描述
该算法具体如下:对一个长度为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……x
e……e
e……l
l……p
m……a
p……m
x……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; }