[Swust OJ 138]--文件压缩
题目链接:http://acm.swust.edu.cn/problem/138/
Time limit(ms): 1000 Memory limit(kb): 65535
Description
提高文件的压缩率一直是人们追求的目标。近几年有人提出了这样一种算法,它虽然只是单纯地对文件进行重排,本身并不压缩文件,但是经这种算法调整后的文件在大多数情况下都能获得比原来更大的压缩率。 该算法具体如下:对一个长度为n的字符串S,首先根据它构造n个字符串,其中第i个字符串由将S的前i-1个字符置于末尾得到。然后把这n个字符串按照首字符从小到大排序,如果两个字符串的首字符相等,则按照它们在S中的位置从小到大排序。排序后的字符串的尾字符可以组成一个新的字符串S’,它的长度也是n,并且包含了S中的每一个字符。最后输出S’以及S的首字符在S’中的位置p。
举例: S: example
1、构造n个字符串 example xamplee ampleex mpleexa pleexam leexamp eexampl
2、将字符串排序 ampleex example eexampl leexamp mpleexa pleexam xamplee
3、压缩结果 S’= xelpame ,p=7
由于英语单词构造的特殊性,某些字母对出现的频率很高,因此在S’中相同的字母有很大几率排在一起,从而提高S’的压缩率。虽然这种算法利用了英语单词的特性,然而在实践的过程中,人们发现它几乎适用于所有的文件压缩。
请你编一个程序,读入S’和p,输出字符串S。
Input
输入文件共有三行,第1行是一个整数n(1<=n<=10000),代表S’的长度,第2行是字符串S’,第3行是整数p。
Output
仅包含一行S。
Sample Input
7
xelpame
7
|
Sample Output
example |
解题思路:这道题感觉太诡异了(想得整个人都不好了~~),这是一个逆向求解得过程,就好比密码破译,只不过你知道了关键的key;
(1)观察没有排序的已经构造好的序列前一个字符串的第一个字符是下一个字符串的最后一个字符;
(2)由于原始单词第一个字符位置已知,而我们通过对转换好的字符排序,得到了排序过后的字符串的首字母;
(3)排序时是首字母相同按出现次序先后排序;
我们通过以上3点从原始字符的最后一个出发利用(1)的规则结合(2)的特性,找到排列好的字符串第一个字符与当前标识符(初始是第一个字符,可以查找到原始序列最后一个字符)相等的字符串,其最后字符(输入的字符串对应位置的元素)为所求,以此类推,逆序得到完整原始字符串,然后倒序输出;
主要代码如下:
for (i = 0; i < len; i++) if (ss[i] == c)break; ans[0] = se[i]; c = se[i]; ss[i] = '#'; for (i = len - 1; i >= 0; i--){ if (ss[i] == c){ c = se[i]; ans[cnt++] = c; ss[i] = '#'; i = len; } }
结合上面三点仔细体会~~~
完整代码如下:
1 #include <iostream> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 int main(){ 6 int len, p, i; 7 while (cin >> len){ 8 char se[10001], ss[10001], ans[10001], c; 9 int cnt = 1; 10 cin >> se >> p; 11 if (len == 1){ 12 cout << se << endl; 13 continue; 14 } 15 strcpy(ss, se); 16 sort(ss, ss + len); 17 c = se[p - 1]; 18 for (i = 0; i < len; i++) 19 if (ss[i] == c)break; 20 ans[0] = se[i]; 21 c = se[i]; 22 ss[i] = '#'; 23 for (i = len - 1; i >= 0; i--){ 24 if (ss[i] == c){ 25 c = se[i]; 26 ans[cnt++] = c; 27 ss[i] = '#'; 28 i = len; 29 } 30 } 31 for (i = len - 1; i >= 0; i--) 32 cout << ans[i]; 33 cout << endl; 34 } 35 return 0; 36 }
如果这是你所爱的,就不要让自己后悔~~~