【LeetCode】68. 文本左右对齐

leetcode

 

解题思路

文本左右对齐问题需要将单词数组按最大宽度排版,满足左右对齐规则,最后一行左对齐。核心思路是 ​​贪心算法确定每行单词数​​ + ​​动态空格分配​​,具体步骤如下:

  1. ​​确定每行单词范围​​:贪心选择尽可能多的单词,确保单词总长度加最小空格数不超过 maxWidth
  2. ​​生成每行字符串​​:
    • ​​普通行​​:均匀分配空格,左侧优先多放空格。
    • ​​最后一行或单单词行​​:左对齐,行末补剩余空格。

关键步骤

  1. ​​行划分​​:通过遍历确定每行的起始和结束单词索引,并计算总单词长度。
  2. ​​空格分配​​:
    • 普通行:总空格数按单词间隔分配,余数优先给左侧。
    • 最后一行:单词间仅一个空格,剩余空格补在末尾。
  3. ​​高效构建字符串​​:使用 strings.Builder 避免频繁字符串拼接的性能问题。

复杂度分析

  • ​​时间复杂度​​:O(n),每个单词被遍历一次。
  • ​​空间复杂度​​:O(m),存储结果数组的长度(m 为行数)。

代码实现

func fullJustify(words []string, maxWidth int) []string {
    var result []string
    start := 0
    n := len(words)

    for start < n {
        // 确定当前行的结束索引和单词总长度
        end, sumWordLen := findEnd(words, start, maxWidth)
        // 生成当前行的字符串
        line := buildLine(words, start, end, sumWordLen, maxWidth, end == n-1)
        result = append(result, line)
        start = end + 1
    }
    return result
}

// 找到当前行的结束索引和单词总长度
func findEnd(words []string, start, maxWidth int) (int, int) {
    sumWordLen := 0
    end := start
    for end < len(words) {
        currentWordLen := sumWordLen + len(words[end]) // 尝试添加当前单词
        requiredSpace := end - start                   // 当前行已包含的空格数(单词数-1)
        totalLength := currentWordLen + requiredSpace  // 总长度(单词+空格)
        if totalLength > maxWidth {                    // 超过最大宽度则停止
            return end - 1, sumWordLen
        }
        sumWordLen = currentWordLen // 更新总单词长度
        end++
    }
    return end - 1, sumWordLen // 处理到最后一个单词的情况
}

// 构建单行字符串
func buildLine(words []string, start, end, sumWordLen, maxWidth int, isLastLine bool) string {
    var builder strings.Builder
    wordCount := end - start + 1
    totalSpaces := maxWidth - sumWordLen

    // 处理最后一行或单单词行:左对齐,末尾补空格
    if wordCount == 1 || isLastLine {
        for i := start; i <= end; i++ {
            if i > start {
                builder.WriteByte(' ')
            }
            builder.WriteString(words[i])
        }
        remainingSpaces := maxWidth - builder.Len()
        builder.WriteString(strings.Repeat(" ", remainingSpaces))
        return builder.String()
    }

    // 普通行:均匀分配空格,左侧优先多放
    slots := wordCount - 1
    baseSpace := totalSpaces / slots
    extraSpace := totalSpaces % slots

    builder.WriteString(words[start])
    for i := 0; i < slots; i++ {
        space := baseSpace
        if i < extraSpace { // 前 extraSpace 个间隙多1空格
            space++
        }
        builder.WriteString(strings.Repeat(" ", space))
        builder.WriteString(words[start+i+1])
    }
    return builder.String()
}

代码解析

  1. ​​行划分逻辑​​ (findEnd):
    • 逐个添加单词,计算当前行的总长度(单词长度 + 最小空格数),直到超过 maxWidth
    • 返回当前行的结束索引和单词总长度。
  2. ​​字符串构建​​ (buildLine):
    • ​​最后一行/单单词行​​:左对齐,末尾补足空格。
    • ​​普通行​​:计算每个间隔的基础空格数和余数,优先将余数分配给左侧间隔。
  3. ​​高效字符串操作​​:使用 strings.Builder 减少内存分配次数,提升性能。

运行示例

func main() {
    // 示例1
    words1 := []string{"This", "is", "an", "example", "of", "text", "justification."}
    fmt.Println(fullJustify(words1, 16))
    // 输出: [This    is    an example  of text justification.  ]

    // 示例2
    words2 := []string{"What", "must", "be", "acknowledgment", "shall", "be"}
    fmt.Println(fullJustify(words2, 16))
    // 输出: [What   must   be acknowledgment   shall be        ]
}

关键点

  • ​​贪心选择行单词​​:确保每行尽可能多放单词,避免回溯。
  • ​​空格分配策略​​:普通行均匀分配余数空格,最后一行强制左对齐。
  • ​​边界处理​​:单单词行和最后一行需特殊处理,避免多余计算。
posted @ 2025-04-25 11:15  云隙之间  阅读(39)  评论(0)    收藏  举报