贪心算法 | 字典序最小的美丽字符串

学术   科技   2023-05-19 06:33   北京  

题目(难度困难)

    如果一个字符串满足以下条件,则称其为美丽字符串

  • 它由英语小写字母表的 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" 的字符串。

提示

  • 1 <= n == s.length <= 10^5

  • 4 <= k <= 26

  • s 是一个美丽字符串

字典序概念

    字典序,就是按照字典中出现的先后顺序进行排序。

    这是单个字符的大小情况,那么如果是两个字符串比较大小呢?在计算机中,两个字符串比较大小,是按照从左到右的顺序进行比较,如果第1位相等,就比较第2位,直至有一位可以比较出大小来,则不再继续比较。

贪心算法

    这题难点在于分析题意,如果能知道思路,其实这题不算很难!

    长度为2 的回文串就是两个相邻的相同字符,长度为 3的回文串就是下标之差为 2 的两个相同字符。因此,一个字符串是美丽的,当且仅当其中的每个字符与它前面两个字符都不相同

    为了找到比 s 大的,同时又是最小的美丽字符串,我们尝试先从最后一个字符 s[n - 1] 开始修改。因为答案要比 s 大,所以我们只能让 s[n - 1] 变大,且变化后不能和 s[n - 2] 以及 s[n - 3] 相同。

    如果我们能在字母表前k 个字符中找到符合要求的字符,那么我们只要修改s[n - 1] 就行了,否则我们还得修改 s[n - 2]。因为答案要比 s 大,所以我们只能让 s[n - 2] 变大,且变化后不能和 s[n - 3] 以及 s[n - 4] 相同。

    从上述分析可以看出来,修改每个字符的流程都是一样的:如果我们要修改 s[i],可以首先枚举字母表的前 k 个字符,看能否找到比 s[i] 大,但又和 s[i - 1] 以及 s[i - 2] 不同的字符。如果找到了即可结束流程,否则我们还得修改 s[i - 1],重复上述流程。如果已经改到 s[0] 还无法满足要求,那就是无解。

    如果修改 s[i] 就能满足要求,由于 s[i] 已经比原来大了,后面的字符不管填什么,答案都会比 s 大。因此 s[i + 1] s[n - 1] 只要贪心地填入与前两个字符不同的最小字符即可。因为 k≥4,我们总能找到这样的字符。

    复杂度O(n)

代码

class Solution {public:    string smallestBeautifulString(string s, int k) {        int n = s.size();                // 这个函数从 s[n - 1] 开始尝试修改,返回最少要修改到哪个位置 + 1        auto gao = [&]() {            for (int i = n - 1; i >= 0; i--) {                // 枚举比 s[i] 大,且是字母表前 k 个的字符                for (int j = s[i] + 1; j < k + 'a'; j++) {                    // 当前字符和前两个字符中的某一个相同,不行                    if ((i >= 1 && j == s[i - 1]) || (i >= 2 && j == s[i - 2])) continue;                    // 找到了一个可行字符,修改 s[i]                    s[i] = j;                    return i + 1;                }            }            // 改 s[0] 都不行,无解            return -1;        };        int pos = gao();            // 最少修改的位置        if (pos == -1) return "";   // 无解                // s[pos] 到 s[n - 1] 贪心填入与前两个字符不同的最小字符即可        for (int i = pos; i < n; i++) {            // 枚举字母表前 k 个字符            for (int j = 'a'; j < k + 'a'; j++) {                // 当前字符和前两个字符中的某一个相同,不行                if ((i >= 1 && j == s[i - 1]) || (i >= 2 && j == s[i - 2])) continue;                // 找到了一个可行字符,修改 s[i],并继续修改下一个字符                s[i] = j;                break;            }        }        return s;    }};

题目链接

https://leetcode.cn/problems/lexicographically-smallest-beautiful-string

参考链接

https://leetcode.cn/problems/lexicographically-smallest-beautiful-string/solution/tan-xin-by-tsreaper-e7ny/

控制工程研习
好好学习,天天向上
 最新文章