最长快乐字符串

标签: 贪心 字符串 堆(优先队列)

难度: Medium

如果字符串中不含有任何 'aaa''bbb''ccc' 这样的字符串作为子串,那么该字符串就是一个「快乐字符串」。

给你三个整数 abc,请你返回 任意一个 满足下列全部条件的字符串 s

  • s 是一个尽可能长的快乐字符串。
  • s最多a 个字母 'a'b 个字母 'b'c 个字母 'c'
  • s 中只含有 'a''b''c' 三种字母。

如果不存在这样的字符串 s ,请返回一个空字符串 ""

示例 1:

输入:a = 1, b = 1, c = 7
输出:"ccaccbcc"
解释:"ccbccacc" 也是一种正确答案。

示例 2:

输入:a = 2, b = 2, c = 1
输出:"aabbc"

示例 3:

输入:a = 7, b = 1, c = 0
输出:"aabaa"
解释:这是该测试用例的唯一正确答案。

提示:

  • 0 <= a, b, c <= 100
  • a + b + c > 0

Submission

运行时间: 20 ms

内存: 16.1 MB

class Solution:
    def longestDiverseString(self, a: int, b: int, c: int) -> str:
        maxm = max(a,b,c)
        if maxm == 0:
            return ''
        if maxm == a:
            s = 'a'
        elif maxm == b:
            s = 'b'
        else: s = 'c'
        if maxm % 2 == 0:
            can = [s*2 for _ in range(maxm//2)]
        else:
            can = [s*2 for _ in range(maxm//2)] + [s]
        
        if s == 'a':
            if len(can) - b - c >= 2:
                can = can[:-(len(can) - b - c)+1]
            j = 0
            while b != 0 or c != 0:
                if b != 0:
                    can[j] = can[j] + 'b'
                    j = (j+1) % len(can)
                    b -= 1
                    continue
                if c != 0:
                    can[j] = can[j] + 'c'
                    j = (j+1) % len(can)
                    c -= 1
                    continue
        elif s == 'b':
            if len(can) - a - c >= 2:
                can = can[:-(len(can) - a - c)+1]
            j = 0
            while a != 0 or c != 0:
                if a != 0:
                    can[j] = can[j] + 'a'
                    j = (j+1) % len(can)
                    a -= 1
                    continue
                if c != 0:
                    can[j] = can[j] + 'c'
                    j = (j+1) % len(can)
                    c -= 1
                    continue
        else:
            if len(can) - a - b >= 2:
                can = can[:-(len(can) - a - b)+1]
            j = 0
            while a != 0 or b != 0:
                if a != 0:
                    can[j] = can[j] + 'a'
                    j = (j+1) % len(can)
                    a -= 1
                    continue
                if b != 0:
                    can[j] = can[j] + 'b'
                    j = (j+1) % len(can)
                    b -= 1
                    continue
        return ''.join(can)

        

Explain

该题解的思路是先找出 a, b, c 中的最大值 maxm,根据 maxm 的值确定主要构成字符串的字母 s。然后根据 maxm 的奇偶性,构造出由 s 组成的子串列表 can,使得每个子串最多包含两个连续的 s。接下来,根据 s 的值,分三种情况在 can 的子串中穿插剩余的字母,每次穿插一个字母,并移动到下一个子串,直到剩余字母用尽。最后将 can 中的所有子串拼接起来即可得到最长的快乐字符串。

时间复杂度: O(a + b + c)

空间复杂度: O(a + b + c)

class Solution:
    def longestDiverseString(self, a: int, b: int, c: int) -> str:
        # 找出 a, b, c 中的最大值 maxm
        maxm = max(a,b,c)
        if maxm == 0:
            return ''
        
        # 根据 maxm 确定主要构成字符串的字母 s
        if maxm == a:
            s = 'a'
        elif maxm == b:
            s = 'b'
        else: s = 'c'
        
        # 根据 maxm 的奇偶性构造子串列表 can
        if maxm % 2 == 0:
            can = [s*2 for _ in range(maxm//2)]
        else:
            can = [s*2 for _ in range(maxm//2)] + [s]
        
        # 在 can 中穿插剩余字母
        if s == 'a':
            # 如果 can 的长度减去 b 和 c 的数量大于等于 2,则需要减少 can 的长度
            if len(can) - b - c >= 2:
                can = can[:-(len(can) - b - c)+1]
            j = 0
            while b != 0 or c != 0:
                if b != 0:
                    can[j] = can[j] + 'b'
                    j = (j+1) % len(can)
                    b -= 1
                    continue
                if c != 0:
                    can[j] = can[j] + 'c'
                    j = (j+1) % len(can)
                    c -= 1
                    continue
        elif s == 'b':
            if len(can) - a - c >= 2:
                can = can[:-(len(can) - a - c)+1]
            j = 0
            while a != 0 or c != 0:
                if a != 0:
                    can[j] = can[j] + 'a'
                    j = (j+1) % len(can)
                    a -= 1
                    continue
                if c != 0:
                    can[j] = can[j] + 'c'
                    j = (j+1) % len(can)
                    c -= 1
                    continue
        else:
            if len(can) - a - b >= 2:
                can = can[:-(len(can) - a - b)+1]
            j = 0
            while a != 0 or b != 0:
                if a != 0:
                    can[j] = can[j] + 'a'
                    j = (j+1) % len(can)
                    a -= 1
                    continue
                if b != 0:
                    can[j] = can[j] + 'b'
                    j = (j+1) % len(can)
                    b -= 1
                    continue
        
        # 拼接 can 中的所有子串得到最终的快乐字符串
        return ''.join(can)

Explore

在构造快乐字符串时,一个重要的规则是同一个字符不能连续出现超过两次。当maxm(即a,b,或c中的最大值)是偶数时,可以将s(最大数量的字符)均匀地分布为多个长度为2的子串。如果maxm是奇数,除了偶数次的子串外,还需要额外添加一个长度为1的s,以确保所有字符都能被使用并且尽可能均匀地分布。

这个条件主要是为了防止在can列表中插入过多其他字符导致某些子串超过两个字符的限制。如果剩余的字符(比如b和c,当s为'a')数量少于can列表的长度减去2,则can列表的长度可能需要调整,以避免在穿插过程中某个子串变得过长。这有助于维持快乐字符串的规则,即避免任何字符连续出现超过两次。

在理想情况下,该while循环不应该造成无限循环,因为循环的条件是穿插剩余的字符,而每次循环都会减少至少一个字符的计数(b或c)。然而,如果循环的实现有逻辑错误或者条件更新不正确(例如忘记减少字符计数),则可能导致无限循环。正确的实现应当确保每个字符都在循环中逐个被减少,直至全部插入完毕。

采用模运算确实能保证索引始终在can的有效范围内,防止索引越界。但这种方法并不总是能保证剩余字母完全均匀分布,尤其是当剩余字母的数量与can列表的长度不成比例时。这种情况下,某些子串可能会集中更多的插入字符,尤其是在剩余字符数量较多或can列表较短时。为了尽可能均匀地分布字母,可能还需要考虑其他分布策略或在can列表构建时预先考虑更均匀的分布。