1663. 具有给定数值的最小字符串【贪心算法】

Posted by CodingWithAlice on June 22, 2021

1663. 具有给定数值的最小字符串【贪心算法】

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

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

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

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

1、x 是 y 的一个前缀;

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

  • 方法一:自己写的算法

image-20210623000016342

时间复杂度:O(n),空间复杂度:xx

var getSmallestString = function (n, k) {
        // 返回一个数组:长度为n,值为val
        let setArr = (length, value) => {
            if (length <= 0) {
                return [];
            }
            let arr = [];
            for(let i = 0;i < length; i++) {
                arr.push(value);
            }
            return arr;
        }
        // 返回数字对应的字母
        let getLetter = (num) => {
            if (num === 0) {
                return  ''
            }
            // A - 65; a - 97
            return String.fromCharCode(num + 96);
        }

        let left = k % 26; // 余数
        let mul = Math.floor(k / 26); // 倍数
        
        // mul 位值为 26 数组
        let max = setArr(mul, 'z'); 

        // 余数全部拆解 + mul 能够补齐 n 位
        let ones = 0;
        let mid = 0;
        if((left + mul) === n) {
            // 刚好补齐,直接返回 z 
            let min = setArr(left, 'a'); // 多个值为 1 的数组
            return [...min, ...max].join('');
        } else if((left + mul) > n ) {
            // 可以补齐
            ones = n - mul - 1;
            mid = left - ones;
        } else {
            // 不能直接补齐,找 z 补齐
            ones = n - mul - 1;
            let zNum = ones;
            do {
                max.shift();
                ones += 1; // z 的数量变少了
                zNum += 1; // zNum 代表的是 ones,所以 ones + 1,zNum 也要 + 1
                zNum = zNum > 26 ? (zNum - 26) : ones;
            }while(zNum > 26)
            mid = k - (ones + 26*(max.length));
            if (mid < 0) {
                mid += 26;
                mid -= 1; // 倒数过来序号问题
                ones += 1; // z 的数量变少了
                max.shift();
            }
            // 缺少一位
            if(mid === 0) {
                ones += 1; // z 的数量变少了
                mid = 25;
                max.shift();
            }
        }

        let min = setArr(ones, 'a'); // 多个值为 1 的数组
        let midChar = getLetter(mid);
        return [...min, midChar, ...max].join('');
    };
  • 方法二:贪心算法

image-20210624083525936

时间复杂度:O(n),空间复杂度:xx

// 策略:先全部都填充为 a,再从后往前填充z
var getSmallestString = (n, k) => {
  	// 全部填充为a --> 初始解
  	let arr = [];
  	for (let i = 0; i < n; i++) {
    		arr.push('a');
  	}
  
  	// n-- 相当于结果数组从后往前
  	while (n > 0) {
    		// n - 1 是 a 的个数 (n-1)个a
    		if (k - 26 >= n - 1) {
      			// k-26 的结果是否大于所需要的 a 的个数 -> 大于就赋值 26
      			arr[n - 1] = 'z';
      			k -= 26;
    		} else {
      			// 相当于k的值已经不能再减26了,否则n数量就不够了
      			// n - 1 是 a 的数量;由于ASC码从 a-97 算起,k 是 数量,从1算起,所以 97 - 1 
      			arr[n - 1] = String.fromCharCode((97 - 1) + k - (n - 1));
      			break
    		}
    		n--;
  	}
 		return arr.join('');
}