具有给定数值的最小字符串

标签: 贪心 字符串

难度: Medium

小写字符 数值 是它在字母表中的位置(从 1 开始),因此 a 的数值为 1b 的数值为 2c 的数值为 3 ,以此类推。

字符串由若干小写字符组成,字符串的数值 为各字符的数值之和。例如,字符串 "abe" 的数值等于 1 + 2 + 5 = 8

给你两个整数 nk 。返回 长度 等于 n数值 等于 k字典序最小 的字符串。

注意,如果字符串 x 在字典排序中位于 y 之前,就认为 x 字典序比 y 小,有以下两种情况:

  • xy 的一个前缀;
  • 如果 i 是 x[i] != y[i] 的第一个位置,且 x[i] 在字母表中的位置比 y[i] 靠前。

 

示例 1:

输入:n = 3, k = 27
输出:"aay"
解释:字符串的数值为 1 + 1 + 25 = 27,它是数值满足要求且长度等于 3 字典序最小的字符串。

示例 2:

输入:n = 5, k = 73
输出:"aaszz"

 

提示:

  • 1 <= n <= 105
  • n <= k <= 26 * n

Submission

运行时间: 35 ms

内存: 16.5 MB

class Solution:
    def getSmallestString(self, n: int, k: int) -> str:
        p,q = divmod(k-n, 25)
        return 'a'*(n-p-1) + chr(ord('a') + q) + 'z'*p if n > p else 'z'*p

Explain

这个题解的策略是首先尽可能地使用字符 'z'(数值为 26),因为我们想要字典序最小的字符串,所以 'z' 应尽可能地放在字符串的末尾。通过计算 k-n,我们可以得知除了全部用 'a' 以外我们还需要多少数值,因为每个 'a' 贡献了 1 点数值,所以 k-n 就是除了全 'a' 字符串外需要的额外数值。然后,这个额外的数值用尽量少的 'z' 字符来构成,每个 'z' 能贡献 25 点(因为 'z' = 26 而一个 'a' 已经计算过了)。divmod(k-n, 25) 可以得到需要多少个 'z' 和剩下的余数,余数将通过一个字符('a' 到 'y' 之间)来补充。最后,构造这个字符串:先放尽可能多的 'a',接着是一个由余数确定的字符,最后是所有的 'z'。

时间复杂度: O(n)

空间复杂度: O(n)

# 定义类和方法
class Solution:
    def getSmallestString(self, n: int, k: int) -> str:
        # 计算需要多少个 'z' 和余数 q
        p, q = divmod(k-n, 25)
        # 构造字典序最小的字符串
        # 如果 'p' 小于 'n',意味着不是所有位置都是 'z'
        return 'a'*(n-p-1) + chr(ord('a') + q) + 'z'*p if n > p else 'z'*p

Explore

在题解的设定中,`k-n` 表示除了每个位置最小可能值 'a' 外,额外需要的总数值。理论上,`k` 应不小于 `n`(因为每个位置最小是 'a',总和至少为 `n`)。如果 `k-n` 为负,这可能表示输入参数不合理,因为 `k` 应至少等于 `n`。在实际应用中应检查输入值,确保 `k >= n`,否则返回输入错误。

如果 `k-n` 小于 25,`divmod(k-n, 25)` 的结果是 `(0, k-n)`。这意味着不需要 'z' 字符,余数 `k-n` 将直接转化为一个对应的字符(从 'a' 到 'y')。在这种情况下,字符串将由 `n-1` 个 'a' 和一个字符 `chr(ord('a') + (k-n-1))` 组成,没有 'z' 字符。

在正常逻辑下,`q` 作为 `divmod(k-n, 25)` 的余数,其值范围是 0 到 24。因此,`q` 不可能超过 25。如果 `q` 超过 25,这将是代码实现或逻辑错误,应重新检查 `divmod` 的使用是否正确。

当 `p` 为 0 时,表示不需要添加任何 'z' 字符。这种情况下,字符串将由 `n-1` 个 'a' 和一个由余数 `q` 确定的字符组成(`chr(ord('a') + q)`),余数 `q` 正是 `k-n` 的值。因此,构建逻辑在 `p` 为 0 时依然有效,只是字符串中不包含 'z' 字符。