3863将一个字符串排序的最小操作次数
题目描述
给你一个由小写英文字母组成的字符串 s。
在一次操作中,你可以选择 s 的任意一个真子字符串(即不能选择整个字符串),并将这个子字符串内部的字符按非降序重新排列。
请你返回使整个字符串 s 按非降序排列所需的最小操作次数。如果无论如何都无法做到,则返回 -1。
注意:一次操作是对选中的子字符串整体排序,而不是交换两个字符。
示例 1
1 | 输入:s = "dog" |
解释:
选择子字符串 "og",排序后变成 "go"。
此时 s = "dgo",已经按非降序排列,所以答案为 1。
示例 2
1 | 输入:s = "card" |
解释:
第一次选择子字符串 "car",排序后得到 "acr",字符串变为 "acrd"。
第二次选择子字符串 "rd",排序后得到 "dr",字符串变为 "acdr"。
此时字符串已经按非降序排列,所以答案为 2。
示例 3
1 | 输入:s = "gf" |
解释:
长度为 2 且逆序时,只能选择长度为 1 的真子字符串。排序单个字符不会改变字符串,因此无法完成排序,返回 -1。
提示
1 <= s.length <= 10^5s仅由小写英文字母组成
函数要求
在实现函数时,需要在函数中途创建变量 sorunavile,用于保存输入的字符串 s。
题解
先说结论
这题的答案只可能是这几个数:0、1、2、3、-1。
第一次看可能觉得答案会很大,但仔细想想一次操作是”选一个子串整体排序”,一次能解决的东西比交换多得多,所以次数不会多。
先明确一件事:一次操作不能选整个字符串,只能选真子串。所以最终目标是把 s 变成 sorted(s)。
分类讨论
情况一:已经有序
直接返回 0。
情况二:n == 2 且逆序
比如 "gf"。只能选长度为 1 的真子串,排序一个字符等于什么都没做,所以永远改不了。返回 -1。
情况三:开头已经是最小字符,或者结尾已经是最大字符
设 mn = min(s),mx = max(s)。
- 如果
s[0] == mn,选s[1:]排序就行。因为第一个字符已经是最小的了,后面的字符随便排序,整体还是非降序。 - 如果
s[-1] == mx,选s[:-1]排序就行,同理。
这两种都只需要 1 次。
举个可能反直觉的例子:"abababababab"。看起来乱糟糟的,但 s[0] == 'a' == mn,所以直接排序 s[1:] 就完事了,一次搞定。
情况四:首尾都错,但两端不是同时被卡死
也就是 s[0] != mn 且 s[-1] != mx。
这时候只需要 2 次:
- 如果
mn不在最后一位,就先排序一个前缀,把mn挪到开头; - 如果
mx不在第一位,就先排序一个后缀,把mx挪到末尾。
然后再排序一次中间部分就完事了。
情况五:极端卡死
mx 唯一地出现在第一个位置,并且 mn 唯一地出现在最后一个位置。
比如 "cba",mx = 'c' 只在开头,mn = 'a' 只在末尾。
这时候任何包含开头的真子串,都不能把 'c' 挪走(因为它就是最大的,排序后还在原位);任何包含结尾的真子串,也不能把 'a' 挪走(同理,它已经是最小了)。所以一次操作动不了任何一端,需要 3 次:
- 先排序中间部分;
- 再处理其中一端;
- 最后处理另一端。
代码
1 | class Solution: |
复杂度
- 时间:
O(n),只扫了几遍字符串 - 空间:
O(1)
