给定一个单词数组 words
和一个长度 maxWidth
,重新排版单词,使其成为每行恰好有 maxWidth
个字符,且左右两端对齐的文本。
你应该使用 “贪心算法” 来放置给定的单词;也就是说,尽可能多地往每行中放置单词。必要时可用空格 ' '
填充,使得每行恰好有 maxWidth 个字符。
要求尽可能均匀分配单词间的空格数量。如果某一行单词间的空格不能均匀分配,则左侧放置的空格数要多于右侧的空格数。
文本的最后一行应为左对齐,且单词之间不插入额外的空格。
注意:
- 单词是指由非空格字符组成的字符序列。
- 每个单词的长度大于 0,小于等于 maxWidth。
- 输入单词数组
words
至少包含一个单词。
示例 1:
输入: 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 <= words.length <= 300
1 <= words[i].length <= 20
words[i]
由小写英文字母和符号组成1 <= maxWidth <= 100
words[i].length <= maxWidth
解题思路:核心思路是尽可能多地将单词放入每行,同时根据要求均匀分配空格。
解决文本排版(Text Justification)问题的关键在于如何将单词组织成行,保证每行的字符数恰好等于给定的 maxWidth
,并且需要满足以下约束:
- 左右对齐:每行的单词之间的空格应均匀分布,左侧的空格数量优先于右侧的空格数量。
- 最后一行处理:最后一行应该左对齐,词之间只插入一个空格,并在末尾填充空格直到
maxWidth。
- 数据结构:用一个列表存放最终的结果。
- 变量:定义一个
index
变量来跟踪当前处理的单词。 - 使用一个循环遍历
words
数组,逐行处理单词。 - 计算可放单词:在每一行中,从
index
开始,尽可能地将更多的单词添加到当前行。 - 条件检查:当添加下一个单词时,确保不超过
maxWidth
,即:totalChars + words[last].length() + (last - index) <= maxWidth
其中totalChars
表示当前行已使用的字符总数,(last - index)
是当前行中已有的单词间的空格数。
对于非最后一行的处理:
- 计算当前行中单词的数量和总字符数。
- 计算该行需要填充的空格数量
numOfSpaces = maxWidth - totalChars
。 - 计算两个空格的数量:每个单词间的基本空格数
spacesBetweenWords = numOfSpaces / (numOfWords - 1)
和额外的空格数量extraSpaces = numOfSpaces % (numOfWords - 1)
。 - 插入单词时,根据计算的空格数量格式化输出。
对于最后一行的处理:
- 如果当前行是最后一行或者只有一个单词,将单词依次拼接,使用一个空格分隔其间,最后在行末补充空格至
maxWidth
。 - 在每一行处理完成后,更新
index
值为last
,以便处理下一个行。 - 如果单词数组的长度为 1 或者最后一行只有一个单词的特殊情况要特别处理。
这种贪心策略通过尽可能多地将单词放入每行,并通过控制空间分配来实现文本内容的排版。这种方法的核心是处理各行的对齐方式和空格的均匀分布,有效利用了 maxWidth
限制条件,同时确保每一行的排版美观可读。
import java.util.ArrayList;
import java.util.List;
public class TextJustification {
public List<String> fullJustify(String[] words, int maxWidth) {
List<String> result = new ArrayList<>();
int n = words.length;
int index = 0;
while (index < n) {
int totalChars = words[index].length();
int last = index + 1;
// 找出可以放入当前行的单词
while (last < n && totalChars + words[last].length() + (last - index) <= maxWidth) {
totalChars += words[last].length();
last++;
}
StringBuilder line = new StringBuilder();
int numOfWords = last - index;
int numOfSpaces = maxWidth - totalChars;
// If it's the last line or only one word in the line
if (last == n || numOfWords == 1) {
for (int i = index; i < last; i++) {
line.append(words[i]);
if (i < last - 1) {
line.append(' '); // 只在单词间加一个空格
}
}
// 补齐到 maxWidth
while (line.length() < maxWidth) {
line.append(' ');
}
} else {
// 计算每个空格的数量
int spacesBetweenWords = numOfSpaces / (numOfWords - 1);
int extraSpaces = numOfSpaces % (numOfWords - 1);
for (int i = index; i < last; i++) {
line.append(words[i]);
if (i < last - 1) {
for (int j = 0; j < spacesBetweenWords; j++) {
line.append(' ');
}
if (extraSpaces > 0) {
line.append(' '); // 额外的空格
extraSpaces--;
}
}
}
}
result.add(line.toString());
index = last; // 更新 index 继续处理下一个行
}
return result;
}
public static void main(String[] args) {
TextJustification tj = new TextJustification();
String[] words1 = {"This", "is", "an", "example", "of", "text", "justification."};
System.out.println(tj.fullJustify(words1, 16));
String[] words2 = {"What", "must", "be", "acknowledgment", "shall", "be"};
System.out.println(tj.fullJustify(words2, 16));
}
}
代码解析:
-
初始化:
- 创建一个
result
列表来存放每一行的结果。
- 创建一个
-
主循环:
- 使用
index
来跟踪当前处理的单词位置。 - 计算可以放入当前行的单词,直到添加下一个单词时超出
maxWidth
。
- 使用
-
处理最后一行和单词数为 1 的情况:
- 如果是最后一行或当前行只有一个单词,直接拼接单词并在行尾补充空格到
maxWidth
。
- 如果是最后一行或当前行只有一个单词,直接拼接单词并在行尾补充空格到
-
处理一般情况:
- 计算单词之间的空格数和多余的空格。
- 根据计算结果填充空格,确保左侧的空格比右侧的多。
-
添加到结果中:
- 将构造好的行字符串添加到
result
中。
- 将构造好的行字符串添加到
- 时间复杂度:O(n),其中 n 是单词的总数,因为我们对每个单词进行一次遍历。
- 空间复杂度:O(n),用于存储结果。