洛谷P1124 文件压缩

给我自信心干烂了说是
题意就是按照结果字符串反推原字符串
比较容易想到的是将字符串排序并和原字符串一一对应,则排序后得到的字符串就是操作2中得到的一堆字符串的首字母序列
所以就相当于我们知道了他们的相邻的关系,因为如果将字符串看成环的话它们的相对位置是永远不会改变的
将所有的(尾字符,首字符)看成有对应关系的二元组,首字符相当于是尾字符的下一个字符
所以我们知道一个字符的话,可以在对应关系里找到它的下一个字符,那么是不是就可以推出来整个字符串
直到这一步都还是比较好想出来的,然后我就意识到,一个字符你存起来以后可能有多个对应关系,比如样例里e可能对应x也可能对应e,那么随便取肯定是不行的,肯定得按照某种顺序取
然后我就卡壳了
事实上正确做法应该是这样的:假设我们输入的字符串和排序后的字符串分别为字符串s2和s1,也就是说由头组成的是s1,尾组成的是s2
样例中的应该是这样
s1——s2
a———x
e——e
e——l
l——p
m——a
p——m
x——e
得到这个之后,我们可以从一个字符尝试推它的下一个字符,方法是在s2找到对应的字符位置为i,然后s1[i]就是他的下一个。反之我们也可以从一个字符推它的上一个,就是在s1找到对应字符为i,s2[i]就是它的上一个字符
题目给定第一个字符位置,我们可以直接推出最后一个的位置,然后反着推到第一个
桥豆麻袋!那这样是如何解决一个字符可能有多个对应的问题的呢?
答案是:因为我们正着推是要在s2里找。s2是无序的,而反着推要在s1里找,s1是有序的。我们在反着推的时候,必然是要求当前字符越靠后越好(因为是从后往前,比如我们现在已经推到了答案字符串结尾是ab,然后想知道a的前一个字符是什么,那么我们在众多对应关系中肯定是选择a位置最靠后的那一个,而排序规则就是按照该字母实际所处位置排的)。
所以我们考虑储存每个字母最靠后的位置,每次取到以后令其--
下面是代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int l[30]={},r[30]={};//存每个字符在排序后字符串中的起始位置和终点位置
int cnt[30];
char ans[maxn];
void youlinaixu()
{
int n;
cin>>n;
string s;
cin>>s;
int p;
cin>>p;
for(int i=0;i<n;i++)
{
cnt[s[i]-'a']++;
}
int pre=0;
for(int i=0;i<26;i++)
{
if(!cnt[i]) continue;
l[i]=r[pre]+1;
r[i]=l[i]+cnt[i]-1;
pre=i;
}
int tot=0,now=l[s[p-1]-'a'];//先找到首字母,字典序相同情况下排最前面
while(tot<n)
{
ans[++tot]=s[now-1];//取当前字符的上一个字符
//然后在排序字符串中找到该字符的最后一次出现位置,用完后--
now=r[s[now-1]-'a']--;
}
for(int i=n;i>0;i--) cout<<ans[i];
}
int main(){
int t=1;
//cin>>t;
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
while (t--)
{
youlinaixu();
}
return 0;
}

浙公网安备 33010602011771号