LeetCode 68:文本左右对齐

LeetCode 68:文本左右对齐

问题定义与核心挑战

给定单词数组 words 和最大行宽 maxWidth,需将单词排版为每行恰好 maxWidth 个字符的文本,满足:

  1. 贪心排版:每行尽可能多放单词(优先填满)。
  2. 空格规则
    • 最后一行或单行单词:左对齐,单词间一个空格,剩余空格补在末尾。
    • 其他行:空格均匀分配 ,若无法均分,左侧间隔的空格数多于右侧

核心思路:分组 + 空格精细化分配

  1. 分组单词:逐行确定能容纳的单词(考虑单词间至少一个空格)。
  2. 空格计算
    • 对每组单词,计算总空格数,区分最后一行/单行普通行的分配逻辑。
  3. 构造行字符串 :根据空格规则拼接单词和空格,确保长度严格为 maxWidth

算法步骤详解

步骤 1:分组单词(贪心排版)

遍历单词,逐步累加,确定每行能容纳的单词:

  • 初始状态currentLine 存储当前行单词,currentLen 记录当前行已用长度(初始为 0)。
  • 遍历逻辑
    • 若当前行空,直接加入单词,currentLen 设为单词长度。
    • 否则,计算加入下一个单词后的长度(需加 1 个空格),若不超过 maxWidth 则加入,否则当前行闭合,开始新行。
java 复制代码
List<List<String>> lines = new ArrayList<>();
List<String> currentLine = new ArrayList<>();
int currentLen = 0;

for (String word : words) {
    if (currentLine.isEmpty()) {
        currentLine.add(word);
        currentLen = word.length();
    } else {
        int nextLen = currentLen + 1 + word.length(); // 加1是单词间的空格
        if (nextLen <= maxWidth) {
            currentLine.add(word);
            currentLen = nextLen;
        } else {
            lines.add(currentLine); // 闭合当前行
            currentLine = new ArrayList<>();
            currentLine.add(word);
            currentLen = word.length();
        }
    }
}
if (!currentLine.isEmpty()) { // 处理最后一行
    lines.add(currentLine);
}
步骤 2:处理每行的空格分配

对每组单词,分两种情况处理:

情况 1:最后一行 或 单行单词(左对齐)
  • 单词间用 1 个空格 连接,剩余空格补在行尾
情况 2:普通行(空格均匀分配)
  • 计算总空格数:totalSpaces = maxWidth - 单词总长度
  • 间隔数:gaps = 单词数 - 1(如 3 个单词有 2 个间隔)。
  • 基础空格:base = totalSpaces / gaps(每个间隔的基本空格数)。
  • 多余空格:extra = totalSpaces % gaps(前 extra 个间隔需多 1 个空格)。
步骤 3:构造行字符串

根据上述规则,拼接单词和空格:

java 复制代码
List<String> result = new ArrayList<>();
for (int i = 0; i < lines.size(); i++) {
    List<String> line = lines.get(i);
    int numWords = line.size();
    int totalSpaces = maxWidth;
    for (String word : line) { // 计算单词总长度,得到需分配的空格数
        totalSpaces -= word.length();
    }

    StringBuilder sb = new StringBuilder();
    if (numWords == 1 || i == lines.size() - 1) { // 最后一行或单行
        sb.append(String.join(" ", line)); // 单词间1个空格
        int remaining = maxWidth - sb.length(); // 补全行尾空格
        for (int j = 0; j < remaining; j++) {
            sb.append(" ");
        }
    } else { // 普通行,均匀分配空格
        int gaps = numWords - 1;
        int base = totalSpaces / gaps;
        int extra = totalSpaces % gaps;
        for (int j = 0; j < numWords; j++) {
            sb.append(line.get(j));
            if (j < gaps) { // 非最后一个单词,加空格
                int spaceCount = base + (j < extra ? 1 : 0); // 前extra个间隔多1个空格
                for (int k = 0; k < spaceCount; k++) {
                    sb.append(" ");
                }
            }
        }
    }
    result.add(sb.toString());
}

完整代码(Java)

java 复制代码
import java.util.ArrayList;
import java.util.List;

class Solution {
    public List<String> fullJustify(String[] words, int maxWidth) {
        // 步骤1:分组单词,确定每行的单词
        List<List<String>> lines = new ArrayList<>();
        List<String> currentLine = new ArrayList<>();
        int currentLen = 0;

        for (String word : words) {
            if (currentLine.isEmpty()) {
                currentLine.add(word);
                currentLen = word.length();
            } else {
                int nextLen = currentLen + 1 + word.length();
                if (nextLen <= maxWidth) {
                    currentLine.add(word);
                    currentLen = nextLen;
                } else {
                    lines.add(currentLine);
                    currentLine = new ArrayList<>();
                    currentLine.add(word);
                    currentLen = word.length();
                }
            }
        }
        if (!currentLine.isEmpty()) {
            lines.add(currentLine);
        }

        // 步骤2:处理每行的空格,构造结果
        List<String> result = new ArrayList<>();
        for (int i = 0; i < lines.size(); i++) {
            List<String> line = lines.get(i);
            int numWords = line.size();
            int totalSpaces = maxWidth;
            for (String word : line) {
                totalSpaces -= word.length();
            }

            StringBuilder sb = new StringBuilder();
            if (numWords == 1 || i == lines.size() - 1) {
                // 最后一行或单行:左对齐,补空格到末尾
                sb.append(String.join(" ", line));
                int remaining = maxWidth - sb.length();
                for (int j = 0; j < remaining; j++) {
                    sb.append(" ");
                }
            } else {
                // 普通行:均匀分配空格,左侧间隔多
                int gaps = numWords - 1;
                int base = totalSpaces / gaps;
                int extra = totalSpaces % gaps;
                for (int j = 0; j < numWords; j++) {
                    sb.append(line.get(j));
                    if (j < gaps) {
                        int spaceCount = base + (j < extra ? 1 : 0);
                        for (int k = 0; k < spaceCount; k++) {
                            sb.append(" ");
                        }
                    }
                }
            }
            result.add(sb.toString());
        }

        return result;
    }
}

关键逻辑解析

1. 分组的贪心策略
  • 优先填满行:每次尝试加入下一个单词,仅当超过 maxWidth 时才闭合当前行,保证每行单词数最多。
2. 空格分配的细节
  • 最后一行 :单词间仅 1 个空格,剩余空格补在末尾(如示例 1 中最后一行 "justification. ")。
  • 普通行 :通过 baseextra 确保空格均匀,左侧间隔多(如示例 1 中第一行 "This is an",两个间隔各 4 个空格)。
3. 边界处理
  • 单行单词(如 ["a"]maxWidth=5):直接左对齐,补 4 个空格到行尾("a ")。

示例验证(以示例 1 为例)

输入words = ["This","is","an","example","of","text","justification."], maxWidth=16

行索引 单词列表 处理逻辑 输出字符串
0 ["This","is","an"] 普通行,空格数 8,间隔 2 → 每个间隔 4 个空格 "This is an"
1 ["example","of","text"] 普通行,空格数 5,间隔 2 → 前 1 个间隔 3 个空格,后 1 个间隔 2 个空格 → "example of text"(实际计算:example(7)+3+of(2)+2+text(4)=7+3+2+2+4=18?不对,哦,重新计算:单词总长度7+2+4=13,maxWidth=16,空格数3。间隔数2,base=1,extra=1。所以前1个间隔2个空格,后1个间隔1个空格 → 7+2+2+1+4=16 → "example of text"
2 ["justification."] 最后一行,左对齐,补 1 个空格 → "justification. "

该方法通过贪心分组精细化空格分配,严格满足题目规则,高效处理所有边界情况,是文本排版问题的经典实现。

相关推荐
今天也好累1 小时前
C 语言基础第16天:指针补充
java·c语言·数据结构·笔记·学习·算法
大千AI助手2 小时前
直接偏好优化(DPO):原理、演进与大模型对齐新范式
人工智能·神经网络·算法·机器学习·dpo·大模型对齐·直接偏好优化
茴香豆的茴3 小时前
转码刷 LeetCode 笔记[1]:3.无重复字符的最长子串(python)
leetcode
徐小夕3 小时前
再也不怕看不懂 GitHub 代码!这款AI开源项目,一键生成交互架构图
前端·算法·github
SirLancelot14 小时前
数据结构-Set集合(一)Set集合介绍、优缺点
java·开发语言·数据结构·后端·算法·哈希算法·set
LZQqqqqo4 小时前
c#_文件的读写 IO
开发语言·c#
YouQian7724 小时前
label 拓扑排序
数据结构·算法
YouQian7724 小时前
(补题)小塔的饭
算法
歌者長門4 小时前
做题笔记:某大讯飞真题28道
java·数据结构·算法
是店小二呀5 小时前
【动态规划 | 多状态问题】动态规划求解多状态问题
算法·动态规划