洛谷P1124 文件压缩

image
给我自信心干烂了说是

题意就是按照结果字符串反推原字符串
比较容易想到的是将字符串排序并和原字符串一一对应,则排序后得到的字符串就是操作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;
}
posted @ 2025-03-15 16:05  miku今天吃什么  阅读(23)  评论(0)    收藏  举报