LeetCode 68.文本左右对齐【困难】

87 阅读3分钟

题干

给定一个单词数组 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",
     因为最后一行应为左对齐,而不是左右两端对齐。       
     第二行同样为左对齐,这是因为这行只包含一个单词。

题解

使用模拟的方法,在遍历words的过程当中,维护一个滑动窗口,标识当前计算的行的开头和结尾的单词,这个时候有三种情况:

  1. 当前行是最后一行,采用左对齐方式,那么每一个单词之间插入一个空格,不够maxWidth则在最后补充空格
  2. 当前行不是最后一行,且当前行只有一个单词,那么在这个单词后补足一定数量的空格,使行长度达到maxWidth
  3. 当前行不是最后一行,且当前行不只有一个单词,那么需要把空格均匀分配在单词中间,且要满足左侧空格数大于右侧空格数。假设当前行可以容纳的空格数为spaceLen,可以维护一个临时的数组spaceNums用来存放各个单词后的空格数量,从左到右循环遍历spaceNumsspaceNums[i] + 1,直到spaceLen0,即可满足条件。
func fullJustify(words []string, maxWidth int) []string {
	var ans []string
	wordNum := len(words)
	// 当前行的字符数量
	lineLen := 0
	// 当前行的单词在原数组中的起始、终止下标
	lineStart, lineEnd := 0, 0
	// 当前已处理的单词数量
	processed := 0
	// 当处理完所有的单词之后退出循环
	for processed < wordNum {
		// 当前行是最后一行的时候,每个单词中间有一个空格,还不够的话在后面补充空格
		if lineEnd == wordNum {
			tmpLine := ""
			// 每个单词后面都增加一个空格
			for i := lineStart; i <= lineEnd-1; i++ {
				tmpLine += words[i]
				if i != lineEnd-1 {
					tmpLine += " "
				}
				processed++
			}
			// 在后面补充空格
			for len(tmpLine) < maxWidth {
				tmpLine += " "
			}
			ans = append(ans, tmpLine)
			continue
		}
		// 当前行不是最后一行的时候,要把空格平均分配在当前行的单词之间
		curWordLen := len(words[lineEnd])
		lineLen += curWordLen
		lineWordNum := lineEnd - lineStart + 1
		// 如果超出了最大的字符数就要立即计算当前行,否则就将右指针往右推,代表当前行还能装下新的单词
		if lineLen+lineWordNum-2 >= maxWidth {
			// 存放当前行的临时变量
			tmpLine := ""
			// 当前行可以容纳的空格总数量
			spaceLen := maxWidth - (lineLen - curWordLen)
			// 将空格均匀的分配到单词中间
			// 用一个数组spaceNums存放当前行每个单词后面应该有多少空格
			// 如果当前行只有一个单词,那么他后面的空格数量应该等于空格总数
			// 否则将空格均匀分配
			spaceNums, pos := make([]int, lineEnd-lineStart-1), 0
			if len(spaceNums) == 0 {
				spaceNums = append(spaceNums, spaceLen)
			} else {
				for spaceLen > 0 {
					spaceNums[pos]++
					spaceLen--
					pos = (pos + 1) % (lineEnd - lineStart - 1)
				}
			}
			for i := lineStart; i <= lineEnd-1; i++ {
				tmpLine += words[i]
				processed++
				if i-lineStart >= len(spaceNums) {
					break
				}
				for j := 1; j <= spaceNums[i-lineStart]; j++ {
					tmpLine += " "
				}
			}
			// 将计算结果加入最终结果中,并将滑动窗口右移
			ans = append(ans, tmpLine)
			lineStart = lineEnd
			lineLen = 0
		} else {
			lineEnd++
		}
	}
	return ans
}