题目描述:
如果一个字符串满足以下条件,则称其为 美丽字符串 :
它由英语小写字母表的前 k 个字母组成。
它不包含任何长度为 2 或更长的回文子字符串。
给你一个长度为 n 的美丽字符串 s 和一个正整数 k 。
请你找出并返回一个长度为 n 的美丽字符串,该字符串还满足:在字典序大于 s 的所有美丽字符串中字典序最小。如果不存在这样的字符串,则返回一个空字符串。
对于长度相同的两个字符串 a 和 b ,如果字符串 a 在与字符串 b 不同的第一个位置上的字符字典序更大,则字符串 a 的字典序大于字符串 b 。
例如,“abcd” 的字典序比 “abcc” 更大,因为在不同的第一个位置(第四个字符)上 d 的字典序大于 c 。
示例 1:
输入:s = “abcz”, k = 26
输出:“abda”
解释:字符串 “abda” 既是美丽字符串,又满足字典序大于 “abcz” 。
可以证明不存在字符串同时满足字典序大于 “abcz”、美丽字符串、字典序小于 “abda” 这三个条件。
示例 2:
输入:s = “dc”, k = 4
输出:“”
解释:可以证明,不存在既是美丽字符串,又字典序大于 “dc” 的字符串。
题目链接:
https://leetcode.cn/problems/lexicographically-smallest-beautiful-string/description/?envType=daily-question&envId=2024-06-22
思路:
当然了,这一道困难题我们还是不出意外的写不出来。
看了官方的答案,练练拍手,直呼内行。
所以在这里写一下,这个令人称赞的解法。
首先我们都可以想到的就是,应该从最后一个开始递增。因为给的序列已经保证了他是个美丽的字符串。所以你想得到最小的,那最原始的做法就是每次增加一位,相当于是k进制。
但是这样是绝对做不出来的,因为k的范围是26,s的长度是1e5,那就是26的1e5次幂,算到明年也算不出来。
那官解是怎样解决这个问题的呢?非常无敌之巧妙。
他认为从最后开始,每次只需要和前两个字符相比,每次增加三个就够,不需要增加更多。
这是为什么呢?因为你要保证你的得到的是美丽字符串,那就是不能含有回文串。如何判断你变了之后的字符串不是回文串呢?你只需要保证前两个位置的字符和当前位置的字符组成的字符串不是回文串就可以了。
理由是,题目给出的已经是不含回文串的美丽字符串,那当你改变最后一个位置的字符时,只有两种可能使得原字符串含有回文串,第一就是和前一个字符组成回文串,第二就是和前两个字符组成回文串。和前三个字符的话,如果和前两个都不组成,那和前三个必然不会组成回文串,因为原始字符串是美丽的,那前面两个字符不是回文的,那即使当前的最后一个位置和前面3个的字符相同,因为前两个不是回文,所以最终也不是回文。
好,理解了为什么要和前两个比,那就理解了为什么最多增加三次。因为最坏的情况就是,第一次增加一个,和前一个字符相同;第二次增加两个,和前一个字符不同和前两个字符相同;那第三次增加三个,和前两个就都不同了。
因此从最后一个位置开始,最多只需要增加3次,如果没到三次就已经到k的限制了,那就需要移到前一个位置,再进行同样的操作,增加三次和前两位进行比较。
通过上面的操作,就可以得到需要改变字符的位置了。但是,这只是第一步。因为我们刚才的操作都只是,保证改变字符的位置往前都是回文串,但是我们没有保证后面也是回文串,因此我们还需要看看后面的字符,保证他们也是回文串。
没错,聪明的你已经猜到了,做法跟上面一样,也是最多看两个位置,最多增加三次就可以了。
从改变位置的下一个位置开始,第一次增加 0 ,看看跟改变位置的字符是否构成回文串,跟前面两个字符是否构成回文串。不构成的话就直接返回答案。构成的话就加 1 再进行比较。如果加 1 还构成的话,那加 2 就是答案了。
代码:
class Solution {
public:
string smallestBeautifulString(string s, int k) {
for(int i = s.length()-1 ; i >= 0 ; i--){
unordered_set<char> blockedCharacters;
for(int j = 1 ; j <= 2 ; j++){
if(i-j >= 0)
blockedCharacters.insert(s[i-j]);
}
for(int j = 1 ; j <= 3 ; j++){
if(s[i]-'a'+j+1 <= k && blockedCharacters.find(s[i]+j) == blockedCharacters.end())
return generate(s,i,j);
}
}
return "";
}
string generate(string s , int idx , int offset){
s[idx] += offset;
for(int i = idx+1 ; i < s.length() ; i++){
unordered_set<char> blockedCharacters;
for(int j = 1 ; j <= 2 ; j++){
if(i-j >= 0)
blockedCharacters.insert(s[i-j]);
}
for(int j = 0 ; j < 3 ; j++){
if(blockedCharacters.find('a' + j) == blockedCharacters.end()){
s[i] = 'a' + j;
break;
}
}
}
return s;
}
};