CodeForces-Largest Subsequence

题目

这题需要交代的细节都在这里写了,由于字多我怕审核改很多次,就直接写在云剪了。


题意

给一个长度为 \(n\) 的序列,每次都要选出一个当前字符串中字典序最大的子序列以供你下一步操作,然后把这个子序列最尾部的那个字母移动到这个子序列的头部,这是一次操作,问你最少操作次数是多少?

题目思路

  1. 首先需要明白这个具体操作是怎么样的。
  2. 其次需要懂得如何找最大子序。
  3. 最后懂得如何推出这个结论。

代码实现

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
string s;
string k;
int t,n,num;
char flag[200005];
int xiabiao[200005];
char  zuihou;
int main() {
	cin >> t;
	while (t--) {
		num = 0;
		cin >> n;
		cin >> s;
		k = s;
		sort(k.begin(), k.end());
		if (k == s) {
			cout << 0 << endl;
			continue;
		}
		zuihou = 1;
		for (int i = s.size() - 1; i >= 0; i--) {
			if (s[i] >= zuihou) {
				zuihou = s[i];
				flag[++num] = s[i];
				xiabiao[num] = i;
			}
		}
		int ans = 0, ans1 = num;
		for (int i = 1; i <= num; i++) {
			if (flag[i] == zuihou) ans++;
		}
		ans1 = num - ans;
		int step = 1, stepmax = num;
		while (step <= num)
		s[xiabiao[stepmax--]] = flag[step++];
		
		if (s == k)
		cout << ans1 << endl;
		else cout << -1 << endl;
		memset(flag, 0, sizeof flag);
		memset(xiabiao, 0, sizeof xiabiao);
	}
	return 0;
}

关于此题一些细节,会在这里细讲:

  1. 第一个小细节:

首先是不可能的情况,题目只给了一组情况:bac 可能会导致认为

只有bac这种单个最大子序才有可能是不可能情况,其实不是的,像bca这种也是-1,但是他有ca这个两位数的最大子序 ,


  1. 第二个小细节:
    就是关于子序列的问题,CodeForces给了介绍:

当且仅当以下条件之一成立时,

字符串 a在词法上比字符串 b 小:a是 b的前缀,但 a≠b;

在 a和 b不同的第一个位置,字符串 a的字母在字母表中出现的时间早于 b中的相应字母。


  1. 第三个小细节
    就是关于操作的具体方式 比如说给了zbca

操作是这样的 先是找到 zca 然后操作成  azc 然后代入到原先的字符串变成了abzc ,注意了 abzc与 zbca 的样,大家可以好好理解这个操作的具体方式,

下一步再找到了 zc  就是右移下 cz  代入回去变成  abcz  

其实我们完全可以简单化,认真思考下,完全就是对最大子序进行操作,不用考虑代入回去这些麻烦的事情,

比如说找到了 zca 变成 azc ,a不用看了因为已经最前且最小,下一步acz,好了我的最大子序列排好了,那我代入回去肯定也是排好的,对吧?


  1. 第四个小细节

仔细看了3后,我们是不是可以发现其实这个操作次数不就是你这个最大子序列操作的次数吗,是的,没错,就是这样的,


但是我们发现zca操作答案是2,明显结论是:就是zca.size-1,为什么-1,因为z不用排嘛,他最大,别人一次一次排好,最大那个只负责往后一直移动即可,


那么问题来了,如果最大的字母有n个呢?那很显然答案是s.size-n,对吧,所以结论是s.size-n个相同最大的字母数,因为那几个相同最大的都是只负责往右移动就行,可以自己想个字符串进行模拟下


  1. 第五个小细节

结论找出来了,现在我们只需要考虑不可能的了,这个很简单,先给原先的排序一遍,存好,最终我们自己手动排号的一模一样的话,那就是可以,否则不行,这里排序用sort,sort是可以对字符串进行排序的哦


  1. 第6个小细节

就是关于如何找这个最大子序列的问题,你可以从头开始找,遇到就存,遇到更大的就扔了,比如说czddeneeeemigec

入c,看到z扔c,现在是z

看到d入d....最终是zdd

看到e,发现e比不过z,比得过d,好了现在是zd..

.........................

这种思路也是可以找到的,不过尾部开始找,肯定更简单且好理解,这一步也是看别人的学来的.

代码就不贴了,在题解那
然后就是一些简单的复制细节,那个while循环就有很好理解的,好了结束,谢谢观看。

posted @ 2025-04-16 20:30  LteShuai  阅读(12)  评论(0)    收藏  举报