「前端刷题」68. 文本左右对齐

377 阅读4分钟

「这是我参与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()方法即可拼接为这一行的字符串

对于最后一行的话,是左对齐,所以除了最后一项每项补齐一个空格即可,其余的空隙全给最后一项后的空间
简易图
leetcode-9.9.png

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;
};