一、题目描述
小写字符 的 数值 是它在字母表中的位置(从 1 开始),因此 a 的数值为 1 ,b 的数值为 2 ,c 的数值为 3 ,以此类推。
字符串由若干小写字符组成,字符串的数值 为各字符的数值之和。例如,字符串 "abe" 的数值等于 1 + 2 + 5 = 8 。
给你两个整数 n 和 k 。返回 长度 等于 n 且 数值 等于 k 的 字典序最小 的字符串。
注意,如果字符串 x 在字典排序中位于 y 之前,就认为 x 字典序比 y 小,有以下两种情况:
- x 是 y 的一个前缀;
- 如果 i 是 x[i] != y[i] 的第一个位置,且 x[i] 在字母表中的位置比 y[i] 靠前。
二、思路分析
首先一个字母代表一个数值分别是对应的是 a-z 和 1-26;
其次是 n 个字母所代表的数值的和为 k;
最后是字符串的排序必须是 a-z 顺序排序;
一般来说第一反应是通过递归回溯探寻所有的可能性,但是计算量太大,不是有效方案,所以 pass。
其实这道题我们可以用贪心来做,先求局部最优解然后拿到全局的最优解。
首先来计算第一位的字母或者说数值是多少:
我们来去计算区间,保证第一个数 x 取值后 (n - 1) * 26 >= k - x 来确保可以使用 n 个字符串的数值来计算出 k 来。
那么以此类推,第二个数 y 取值后需要满足 (n - 2) * 26 >= k - x - y 第三第四直到第 n...
三、AC 代码
var getSmallestString = function(n, k) {
let res = '' // 返回值
// 第一层循环代表要获取 n 个字符串, i 代表位数
for (let i = 1; i <= n; i++) {
// 第二层循环代表数值,通过轮询来判断 (n - i) * 26 >= k - j
// 注意 k 值是变动的,没找到一个符合条件的数值后 k 就会减去该值
for (let j = 1; j <= 26; j++) {
// 判断是否满足求和的条件
if (k - j <= (n - i) * 26) {
k -= j // 满足条件的时候 k 就减去 j 来试图获取下一个字符串数值
res += String.fromCharCode(96 + j) // 将成立的数值转换成字符串,这里也可用字典来做
break // 结束第二层循环并开始新的一轮循环
}
}
}
return res
}
四、总结
核心在于如何每一位字符串取值过后要保证生下的字符串位数的最大数值的和要大于或等于 k 保证可行性。
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情