「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战」。
题目
链接:leetcode-cn.com/problems/te… 给定一个单词数组和一个长度 maxWidth,重新排版单词,使其成为每行恰好有 maxWidth 个字符,且左右两端对齐的文本。
你应该使用“贪心算法”来放置给定的单词;也就是说,尽可能多地往每行中放置单词。必要时可用空格 ' ' 填充,使得每行恰好有 maxWidth 个字符。
要求尽可能均匀分配单词间的空格数量。如果某一行单词间的空格不能均匀分配,则左侧放置的空格数要多于右侧的空格数。
文本的最后一行应为左对齐,且单词之间不插入额外的空格。
说明:
- 单词是指由非空格字符组成的字符序列。
- 每个单词的长度大于 0,小于等于 maxWidth。
- 输入单词数组
words至少包含一个单词。
示例:
输入: words = ["This", "is", "an", "example", "of", "text", "justification."] maxWidth = 16
输出: [ "This is an", "example of text", "justification. " ]
示例 2:
输入: words = ["What","must","be","acknowledgment","shall","be"]
maxWidth = 16
输出: [ "What must be", "acknowledgment ", "shall be " ]
解释: 注意最后一行的格式应为 "shall be " 而不是 "shall be",
因为最后一行应为左对齐,而不是左右两端对齐。
第二行同样为左对齐,这是因为这行只包含一个单词。
示例 3:
输入: words = ["Science","is","what","we","understand","well","enough","to","explain", "to","a","computer.","Art","is","everything","else","we","do"] maxWidth = 20
输出: [ "Science is what we", "understand well", "enough to explain to", "a computer. Art is", "everything else we", "do " ]
解题思路
思路1
题目的难点在怎么填充空格,除了最后一行,其他行都按照左右对齐,均匀填充空格的规则来填充
那假如一行有三个单词,怎么填充剩余需要补齐的空格
均匀填充,也就是每两个单词之间的空格差不可能超过1
假设一行的长度是16,而这一行有三个单词
["This", "is", "an"]
这时我们需要补齐8个空格,但是需要左右对齐,所以只能在This和is后面补充空格
也就是我们可以对数组元素去加工
let fix = 8
let pos = 0
while (fix--) {
queue[pos] += ' '
if (++pos === queue.length - 1) {
// 因为最后一项右对齐,不需要给最后一项补齐空格,此时需要将当前填充的顺位变为0
pos = 0
}
}
// while循环结束也就是加工完这一行的所有元素,都保存在数组中
["This ", "is ", "an"]
// 接下来只需要arr.join()方法即可拼接为这一行的字符串
对于最后一行的话,是左对齐,所以除了最后一项每项补齐一个空格即可,其余的空隙全给最后一项后的空间
简易图
var fullJustify = function(words, maxWidth) {
// 每一行的单词数组
let queue = []
let n = words.length
// 每一行的单词默认一个空格间隙组成的长度
let queueWidth = 0
let res = []
for (let i = 0; i < n; i++) {
let cur = words[i]
// 每次遍历下一个单词时计算新的长度
let newQueueWidth = queueWidth + cur.length + (queueWidth > 0 ? 1 : 0)
// 如果新的长度小于等于最大长度,则说明当前单词可以填充到当前行
if (newQueueWidth <= maxWidth) {
queueWidth = newQueueWidth
queue.push(cur)
} else {
// 如果新的长度大于最大长度,就说明当前单词不能填充到当前行
// 需要填充的空格数
let gap = maxWidth - queueWidth + queue.length - 1
// 当前在哪个索引单词后填充空格
let gapPos = 0
// 当前行最后一个单词的索引
let limit = queue.length - 1
while (gap-- > 0) {
// 补充空格到当前的单词
queue[gapPos] += ' '
// 如果顺位到当前行最后一个单词,不需要补充,顺位给回最左边
if (++gapPos >= limit) {
gapPos = 0
}
}
res.push(queue.join(''))
// 因为新长度大于最大长度,所以需要将当前排除的单词添加到下一行的数组中,同时更新长度
queue = [cur]
queueWidth = cur.length
}
}
// 这里是最后一行,如果最后一行没有排完的话
// 和之前行唯一的区别就是需要重新置位时,每次都pos置位为最后一个单词即可
if (queue.length) {
let gap = maxWidth - queueWidth + queue.length - 1
let gapPos = 0
let limit = queue.length - 1
while (gap-- > 0) {
queue[gapPos] += ' '
if (++gapPos >= limit) {
gapPos = limit
}
}
res.push(queue.join(''))
}
return res
};
思路2
模拟换行
var fullJustify = function (words, maxWidth) {
let ans = [], cur = 0, tmp = [];
for (const w of words) {
// 统计长度 + 当前单词长度 + 最少空格长度
const width = cur + w.length + tmp.length;
// 大于等于maxWidth就需要进行换行
if (width >= maxWidth) {
// 等于说明空格数量刚好是最少的空格长度
if (width === maxWidth) tmp.push(w), cur += w.length;
// 所有的空格数量
const sum = maxWidth - cur;
// 单词间隔数量
const cnt = tmp.length - 1;
// 间隔平均的空格数量
const average = sum / cnt | 0;
// 多出来的空格数量,需要添加到左边
const remain = sum % cnt;
// 生成行
ans.push(tmp.reduce((s, w, i) => s + ' '.repeat(average + (remain + 1 > i ? 1 : 0)) + w).padEnd(maxWidth, ' '));
// 重置
tmp.length = 0;
cur = 0;
}
// 当前单词已处理,要处理下一个单词
if (width === maxWidth) continue;
cur += w.length;
tmp.push(w);
}
// 处理最后一行
if (tmp.length) ans.push(tmp.join(' ').padEnd(maxWidth, ' '));
return ans;
};