题目:给定一个单词数组和一个长度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",因为最后一行应为左对齐,而不是左右两端对齐。第二行同样为左对齐,这是因为这行只包含一个单词。
1. 思考
根据要求,可以发现排版每行文本的原则可以分为以下三种情况:
1. 最后一行:单词左对齐,单词间用一个空格连接,行末用空格填充。
2. 非最后一行,当前行存在不止一个单词时:假定单词字符数wordsCount,空格数spacesCount,需要将空格均匀地分配在单词间,单词间空格至少有avgSpacesCount。
avgSpacesCount = spacesCount / (wordsCount - 1)
假定多出来的空格数extraSpacesCount。
extraSpacesCount = spacesCount % (wordsCount - 1)
前extraSpacesCount个单词间,应该填充avgSpacesCount + 1个空格,其他单词间填充avgSpacesCount个空格。
3. 非最后一行,当前行只有一个单词时:单词左对齐,行末用空格填充。
2. 代码实现
public List<String> fullJustify(String[] words, int maxWidth) {
List<String> result = new ArrayList<>();
int right = 0;
while (true) {
// 当前批次起始单词索引
// 这样需要每批次right指向最后单词的索引 + 1
int left = right;
// 当前批次单词字符数
int wordsCount = 0;
// 找到当前批次的终止单词索引,right为索引位置 + 1
while (right < words.length && (wordsCount + words[right].length() + right - left - 1) < maxWidth) {
wordsCount += words[right++].length();
}
// 最后一行
if (right == words.length) {
// [left, right)间单词用单个空格连接
StringBuilder sb = join(words, left, right, blank(1));
sb.append(blank(maxWidth - sb.length()));
result.add(sb.toString());
return result;
}
// 非最后一行
if (left == (right - 1)) {
// 非最后一行,只有一个单词
// 单词后面用空格填充
StringBuilder sb = new StringBuilder(words[left]);
sb.append(blank(maxWidth - sb.length()));
result.add(sb.toString());
continue;
}
// 非最后一行,不止一个单词
// 空格总数
int spacesCount = maxWidth - wordsCount;
// 平均连接空格数
int avgSpacesCount = spacesCount / (right - 1 - left);
// 额外添加一个空格的连接点数量
int extraSpacesCount = spacesCount % (right - 1 - left);
// [left, left + extraSpacesCount + 1)间单词用avgSpacesCount + 1个空格连接
StringBuilder sb = join(words, left, left + extraSpacesCount + 1, blank(avgSpacesCount + 1));
sb.append(blank(avgSpacesCount));
// [left + extraSpacesCount + 1, right)间单词用avgSpacesCount个空格连接
sb.append(join(words, left + extraSpacesCount + 1, right, blank(avgSpacesCount)));
result.add(sb.toString());
}
}
private String blank(int count) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) {
sb.append(' ');
}
return sb.toString();
}
// 将[left, right)间单词,用joint连接
private StringBuilder join(String[] words, int left, int right, String joint) {
StringBuilder sb = new StringBuilder(words[left]);
for (int i = left + 1; i < right; i++) {
sb.append(joint);
sb.append(words[i]);
}
return sb;
}