【LeetCode】68. 文本左右对齐
文章目录
- 68. 文本左右对齐
- 题目描述
- 示例 1:
- 示例 2:
- 示例 3:
- 提示:
- 解题思路
- 问题深度分析
- 问题本质
- 核心思想
- 关键难点分析
- 典型情况分析
- 算法对比
- 算法流程图
- 主算法流程(贪心+分段格式化)
- 贪心行分割详细流程
- 两端对齐格式化流程
- 复杂度分析
- 时间复杂度详解
- 空间复杂度详解
- 关键优化技巧
- 技巧1:贪心+分段格式化(最优解法)
- 技巧2:空格均匀分配算法
- 技巧3:左对齐实现
- 技巧4:使用strings.Builder优化
- 边界情况处理
- 测试用例设计
- 基础测试
- 空格不均匀分配
- 多行文本
- 边界情况
- 常见错误与陷阱
- 错误1:忘记最后一行左对齐
- 错误2:空格分配不均匀
- 错误3:单个单词行没有特殊处理
- 错误4:行长度计算错误
- 实战技巧总结
- 进阶扩展
- 扩展1:右对齐版本
- 扩展2:居中对齐版本
- 扩展3:Markdown表格对齐
- 应用场景
- 代码实现
- 测试结果
- 核心收获
- 应用拓展
- 完整题解代码
68. 文本左右对齐
题目描述
给定一个单词数组 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
解题思路
问题深度分析
这是一道文本格式化问题,核心在于贪心算法和空格分配策略。虽然题目看似简单,但涉及到行分割、空格均匀分配、特殊情况处理等多个细节,是理解文本排版和字符串处理的经典问题。
问题本质
给定单词数组和最大宽度,需要将单词排版成每行恰好maxWidth个字符的文本,并满足以下要求:
- 贪心放置:每行尽可能多地放置单词
- 左右对齐:空格均匀分配在单词之间
- 左侧优先:空格不能均匀分配时,左侧多于右侧
- 最后一行:左对齐,单词间只有一个空格
核心思想
三步走策略:
- 行分割:使用贪心算法确定每行包含哪些单词
- 空格计算:计算单词间需要多少空格
- 格式化输出:根据是否最后一行采用不同的对齐策略
关键难点分析
难点1:贪心行分割
- 每行尽可能多地放置单词
- 需要考虑单词之间至少有1个空格
- 判断条件:
当前行长度 + 单词长度 + 已有单词数 <= maxWidth
难点2:空格分配
- 总空格数 = maxWidth - 所有单词长度之和
- 空格间隙数 = 单词数 - 1
- 基础空格数 = 总空格数 / 间隙数
- 额外空格数 = 总空格数 % 间隙数
- 额外空格从左侧开始分配
难点3:特殊情况处理
- 只有1个单词:左对齐,右侧填充空格
- 最后一行:左对齐,单词间只有1个空格
- 单词刚好填满:无需额外空格
典型情况分析
情况1:正常行(多个单词,空格均匀)
words: ["This", "is", "an"]
maxWidth: 16
输出: "This is an"
分析: - 单词总长度: 4+2+2=8- 总空格数: 16-8=8- 间隙数: 2- 每个间隙: 8/2=4个空格
情况2:正常行(空格不均匀)
words: ["example", "of", "text"]
maxWidth: 16
输出: "example of text"
分析:- 单词总长度: 7+2+4=13- 总空格数: 16-13=3- 间隙数: 2- 基础空格: 3/2=1- 额外空格: 3%2=1(给第一个间隙)- 第一个间隙: 1+1=2个空格- 第二个间隙: 1个空格
情况3:单个单词
words: ["acknowledgment"]
maxWidth: 16
输出: "acknowledgment "
分析: 左对齐,右侧填充空格
情况4:最后一行
words: ["shall", "be"]
maxWidth: 16
输出: "shall be "
分析: 左对齐,单词间1个空格,右侧填充
算法对比
算法 | 时间复杂度 | 空间复杂度 | 特点 |
---|---|---|---|
贪心+分段格式化 | O(n) | O(1) | 最优解法,逻辑清晰 |
模拟排版 | O(n) | O(1) | 直接模拟,易理解 |
递归分行 | O(n) | O(h) | 递归实现,h为行数 |
状态机 | O(n) | O(1) | 使用状态机,代码复杂 |
注:n为单词总数,贪心+分段格式化是最优解法
算法流程图
主算法流程(贪心+分段格式化)
贪心行分割详细流程
两端对齐格式化流程
复杂度分析
时间复杂度详解
贪心算法:O(n)
- 遍历所有单词一次:O(n)
- 每个单词只会被处理一次
- 格式化每行的时间与该行单词数成正比
- 总时间:O(n),n为单词总数
各操作复杂度:
- 行分割:O(k),k为当前行单词数
- 空格计算:O(1)
- 字符串拼接:O(k)
- 总体遍历:O(n)
空间复杂度详解
贪心算法:O(1)
- 不考虑结果数组,只使用常数额外空间
- 临时变量:O(1)
- 行缓存:O(maxWidth),常数空间
实际空间:O(n * maxWidth)
- 结果数组:最多n行,每行maxWidth个字符
- 使用StringBuilder可以优化拼接效率
关键优化技巧
技巧1:贪心+分段格式化(最优解法)
func fullJustify(words []string, maxWidth int) []string {result := make([]string, 0)i := 0for i < len(words) {// 确定当前行包含的单词范围 [i, j)j := ilineLen := 0for j < len(words) {newLen := lineLen + len(words[j])if j > i {newLen += 1 // 单词间至少一个空格}if newLen > maxWidth {break}lineLen = newLenj++}// 格式化当前行isLastLine := j == len(words)line := formatLine(words, i, j, maxWidth, isLastLine)result = append(result, line)i = j}return result
}// formatLine 格式化一行
func formatLine(words []string, start, end, maxWidth int, isLastLine bool) string {numWords := end - start// 最后一行或只有一个单词,左对齐if isLastLine || numWords == 1 {return leftAlign(words, start, end, maxWidth)}// 两端对齐return justify(words, start, end, maxWidth)
}
优势:
- 逻辑清晰
- 时间O(n)
- 易于理解和维护
技巧2:空格均匀分配算法
func justify(words []string, start, end, maxWidth int) string {numWords := end - start// 计算单词总长度totalWordsLen := 0for i := start; i < end; i++ {totalWordsLen += len(words[i])}// 计算总空格数和间隙数totalSpaces := maxWidth - totalWordsLengaps := numWords - 1// 每个间隙的基础空格数和额外空格数baseSpaces := totalSpaces / gapsextraSpaces := totalSpaces % gaps// 构建结果var builder strings.Builderfor i := start; i < end; i++ {builder.WriteString(words[i])if i < end-1 {// 添加基础空格for j := 0; j < baseSpaces; j++ {builder.WriteByte(' ')}// 左侧优先,添加额外空格if extraSpaces > 0 {builder.WriteByte(' ')extraSpaces--}}}return builder.String()
}
核心思想:
- 基础空格均匀分配
- 额外空格从左向右分配
- 确保左侧空格多于右侧
技巧3:左对齐实现
func leftAlign(words []string, start, end, maxWidth int) string {var builder strings.Builderfor i := start; i < end; i++ {builder.WriteString(words[i])if i < end-1 {builder.WriteByte(' ')}}// 右侧填充空格for builder.Len() < maxWidth {builder.WriteByte(' ')}return builder.String()
}
适用场景:
- 最后一行
- 只有一个单词的行
技巧4:使用strings.Builder优化
// ❌ 效率低:字符串拼接
result := ""
for _, word := range words {result += word + " "
}// ✅ 高效:使用StringBuilder
var builder strings.Builder
for _, word := range words {builder.WriteString(word)builder.WriteByte(' ')
}
result := builder.String()
优势:
- 避免频繁的字符串复制
- 时间复杂度从O(n²)降为O(n)
边界情况处理
- 单个单词:左对齐,右侧填充空格
- 最后一行:左对齐,单词间只有1个空格
- 单词刚好填满:无需额外空格处理
- 超长单词:题目保证不会出现
- 空数组:题目保证至少有1个单词
测试用例设计
基础测试
输入: ["This", "is", "an", "example", "of", "text", "justification."], maxWidth = 16
输出:
["This is an","example of text","justification. "
]
空格不均匀分配
输入: ["What","must","be","acknowledgment","shall","be"], maxWidth = 16
输出:
["What must be","acknowledgment ","shall be "
]
多行文本
输入: ["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 "
]
边界情况
输入: ["a"], maxWidth = 1
输出: ["a"]输入: ["a", "b"], maxWidth = 3
输出: ["a b"]输入: ["a", "b"], maxWidth = 5
输出: ["a b"]
常见错误与陷阱
错误1:忘记最后一行左对齐
// ❌ 错误:所有行都两端对齐
func fullJustify(words []string, maxWidth int) []string {// ... 所有行都用justify()
}// ✅ 正确:最后一行左对齐
if isLastLine {return leftAlign(words, start, end, maxWidth)
}
错误2:空格分配不均匀
// ❌ 错误:额外空格放在右侧
for i := gaps - 1; i >= 0 && extraSpaces > 0; i-- {spaces[i]++extraSpaces--
}// ✅ 正确:额外空格从左向右
for i := 0; i < gaps && extraSpaces > 0; i++ {spaces[i]++extraSpaces--
}
错误3:单个单词行没有特殊处理
// ❌ 错误:除以0
gaps := numWords - 1 // gaps可能为0
baseSpaces := totalSpaces / gaps // 除以0错误// ✅ 正确:单个单词特殊处理
if numWords == 1 {return leftAlign(words, start, end, maxWidth)
}
错误4:行长度计算错误
// ❌ 错误:没考虑单词间空格
lineLen := 0
for j < len(words) {lineLen += len(words[j])if lineLen > maxWidth {break}
}// ✅ 正确:考虑单词间至少1个空格
newLen := lineLen + len(words[j])
if j > i {newLen += 1 // 单词间空格
}
if newLen > maxWidth {break
}
实战技巧总结
- 贪心行分割:每行尽可能多地放置单词
- 空格计算公式:基础空格 + 额外空格(左侧优先)
- 特殊情况判断:最后一行、单个单词
- StringBuilder优化:避免字符串频繁拼接
- 边界检查:单词间空格、行长度限制
- 代码分离:行分割、格式化分别实现
进阶扩展
扩展1:右对齐版本
// 右对齐:单词靠右,左侧填充空格
func rightAlign(words []string, start, end, maxWidth int) string {var builder strings.Builder// 计算单词总长度totalLen := 0for i := start; i < end; i++ {totalLen += len(words[i])if i > start {totalLen += 1 // 单词间空格}}// 左侧填充空格for i := 0; i < maxWidth-totalLen; i++ {builder.WriteByte(' ')}// 添加单词for i := start; i < end; i++ {if i > start {builder.WriteByte(' ')}builder.WriteString(words[i])}return builder.String()
}
扩展2:居中对齐版本
// 居中对齐:单词居中,两侧填充空格
func centerAlign(words []string, start, end, maxWidth int) string {var builder strings.Builder// 计算单词总长度totalLen := 0for i := start; i < end; i++ {totalLen += len(words[i])if i > start {totalLen += 1}}// 两侧空格totalSpaces := maxWidth - totalLenleftSpaces := totalSpaces / 2rightSpaces := totalSpaces - leftSpaces// 左侧空格for i := 0; i < leftSpaces; i++ {builder.WriteByte(' ')}// 添加单词for i := start; i < end; i++ {if i > start {builder.WriteByte(' ')}builder.WriteString(words[i])}// 右侧空格for i := 0; i < rightSpaces; i++ {builder.WriteByte(' ')}return builder.String()
}
扩展3:Markdown表格对齐
// Markdown表格单元格对齐
func markdownAlign(content string, width int, align string) string {contentLen := len(content)if contentLen >= width {return content[:width]}spaces := width - contentLenswitch align {case "left":return content + strings.Repeat(" ", spaces)case "right":return strings.Repeat(" ", spaces) + contentcase "center":left := spaces / 2right := spaces - leftreturn strings.Repeat(" ", left) + content + strings.Repeat(" ", right)default:return content}
}
应用场景
- 文本编辑器:Word、记事本的对齐功能
- 排版系统:LaTeX、Markdown的文本对齐
- 终端工具:表格输出、日志格式化
- 代码格式化:注释对齐、代码美化
- PDF生成:文档排版、报表生成
代码实现
本题提供了四种不同的解法,重点掌握贪心+分段格式化方法。
测试结果
测试用例 | 贪心+分段 | 模拟排版 | 递归分行 | 状态机 |
---|---|---|---|---|
基础测试 | ✅ | ✅ | ✅ | ✅ |
空格不均匀 | ✅ | ✅ | ✅ | ✅ |
多行文本 | ✅ | ✅ | ✅ | ✅ |
边界测试 | ✅ | ✅ | ✅ | ✅ |
核心收获
- 贪心算法:每行尽可能多地放置单词
- 空格分配:基础空格+额外空格(左侧优先)
- 特殊情况:最后一行左对齐、单个单词处理
- 字符串优化:使用StringBuilder避免频繁拼接
应用拓展
- 文本编辑器对齐功能
- 排版系统实现
- 终端表格输出
- 代码格式化工具
完整题解代码
package mainimport ("fmt""strings"
)// =========================== 方法一:贪心+分段格式化(最优解法) ===========================// fullJustify 贪心+分段格式化
// 时间复杂度:O(n),n为单词总数
// 空间复杂度:O(1),不计结果数组
func fullJustify(words []string, maxWidth int) []string {result := make([]string, 0)i := 0for i < len(words) {// 确定当前行包含的单词范围 [i, j)j := ilineLen := 0for j < len(words) {newLen := lineLen + len(words[j])if j > i {newLen += 1 // 单词间至少一个空格}if newLen > maxWidth {break}lineLen = newLenj++}// 格式化当前行isLastLine := j == len(words)line := formatLine(words, i, j, maxWidth, isLastLine)result = append(result, line)i = j}return result
}// formatLine 格式化一行
func formatLine(words []string, start, end, maxWidth int, isLastLine bool) string {numWords := end - start// 最后一行或只有一个单词,左对齐if isLastLine || numWords == 1 {return leftAlign(words, start, end, maxWidth)}// 两端对齐return justify(words, start, end, maxWidth)
}// justify 两端对齐
func justify(words []string, start, end, maxWidth int) string {numWords := end - start// 计算单词总长度totalWordsLen := 0for i := start; i < end; i++ {totalWordsLen += len(words[i])}// 计算总空格数和间隙数totalSpaces := maxWidth - totalWordsLengaps := numWords - 1// 每个间隙的基础空格数和额外空格数baseSpaces := totalSpaces / gapsextraSpaces := totalSpaces % gaps// 构建结果var builder strings.Builderfor i := start; i < end; i++ {builder.WriteString(words[i])if i < end-1 {// 添加基础空格for j := 0; j < baseSpaces; j++ {builder.WriteByte(' ')}// 左侧优先,添加额外空格if extraSpaces > 0 {builder.WriteByte(' ')extraSpaces--}}}return builder.String()
}// leftAlign 左对齐
func leftAlign(words []string, start, end, maxWidth int) string {var builder strings.Builderfor i := start; i < end; i++ {builder.WriteString(words[i])if i < end-1 {builder.WriteByte(' ')}}// 右侧填充空格for builder.Len() < maxWidth {builder.WriteByte(' ')}return builder.String()
}// =========================== 方法二:模拟排版 ===========================// fullJustify2 模拟排版
// 时间复杂度:O(n)
// 空间复杂度:O(1)
func fullJustify2(words []string, maxWidth int) []string {result := make([]string, 0)currentLine := make([]string, 0)currentLen := 0for i, word := range words {// 检查是否可以加入当前行needLen := currentLen + len(word)if len(currentLine) > 0 {needLen += 1 // 单词间空格}if needLen <= maxWidth {// 可以加入当前行currentLine = append(currentLine, word)currentLen = needLen} else {// 当前行已满,格式化并加入结果line := formatLine2(currentLine, maxWidth, false)result = append(result, line)// 开始新行currentLine = []string{word}currentLen = len(word)}// 最后一个单词if i == len(words)-1 {line := formatLine2(currentLine, maxWidth, true)result = append(result, line)}}return result
}// formatLine2 格式化一行(方法二)
func formatLine2(words []string, maxWidth int, isLastLine bool) string {if len(words) == 0 {return strings.Repeat(" ", maxWidth)}// 最后一行或只有一个单词if isLastLine || len(words) == 1 {line := strings.Join(words, " ")return line + strings.Repeat(" ", maxWidth-len(line))}// 两端对齐totalLen := 0for _, word := range words {totalLen += len(word)}totalSpaces := maxWidth - totalLengaps := len(words) - 1baseSpaces := totalSpaces / gapsextraSpaces := totalSpaces % gapsvar builder strings.Builderfor i, word := range words {builder.WriteString(word)if i < len(words)-1 {spaces := baseSpacesif i < extraSpaces {spaces++}builder.WriteString(strings.Repeat(" ", spaces))}}return builder.String()
}// =========================== 方法三:递归分行 ===========================// fullJustify3 递归分行
// 时间复杂度:O(n)
// 空间复杂度:O(h),h为行数
func fullJustify3(words []string, maxWidth int) []string {return justifyHelper(words, 0, maxWidth)
}// justifyHelper 递归辅助函数
func justifyHelper(words []string, start int, maxWidth int) []string {if start >= len(words) {return []string{}}// 确定当前行包含的单词end := startlineLen := 0for end < len(words) {newLen := lineLen + len(words[end])if end > start {newLen += 1}if newLen > maxWidth {break}lineLen = newLenend++}// 格式化当前行isLastLine := end == len(words)line := formatLine(words, start, end, maxWidth, isLastLine)// 递归处理剩余单词restLines := justifyHelper(words, end, maxWidth)return append([]string{line}, restLines...)
}// =========================== 方法四:预计算空格数组 ===========================// fullJustify4 预计算空格数组
// 时间复杂度:O(n)
// 空间复杂度:O(k),k为每行单词数
func fullJustify4(words []string, maxWidth int) []string {result := make([]string, 0)i := 0for i < len(words) {// 确定当前行包含的单词j := ilineLen := 0for j < len(words) {newLen := lineLen + len(words[j])if j > i {newLen += 1}if newLen > maxWidth {break}lineLen = newLenj++}// 构建当前行line := buildLine(words, i, j, maxWidth, j == len(words))result = append(result, line)i = j}return result
}// buildLine 使用空格数组构建行
func buildLine(words []string, start, end, maxWidth int, isLastLine bool) string {numWords := end - start// 最后一行或单个单词if isLastLine || numWords == 1 {return leftAlign(words, start, end, maxWidth)}// 计算每个间隙的空格数totalLen := 0for i := start; i < end; i++ {totalLen += len(words[i])}totalSpaces := maxWidth - totalLengaps := numWords - 1// 预计算每个间隙的空格数spaces := make([]int, gaps)baseSpaces := totalSpaces / gapsextraSpaces := totalSpaces % gapsfor i := 0; i < gaps; i++ {spaces[i] = baseSpacesif i < extraSpaces {spaces[i]++}}// 构建结果var builder strings.Builderfor i := start; i < end; i++ {builder.WriteString(words[i])if i < end-1 {builder.WriteString(strings.Repeat(" ", spaces[i-start]))}}return builder.String()
}// =========================== 测试代码 ===========================func main() {fmt.Println("=== LeetCode 68: 文本左右对齐 ===\n")// 测试用例testCases := []struct {words []stringmaxWidth intexpect []string}{{words: []string{"This", "is", "an", "example", "of", "text", "justification."},maxWidth: 16,expect: []string{"This is an", "example of text", "justification. "},},{words: []string{"What", "must", "be", "acknowledgment", "shall", "be"},maxWidth: 16,expect: []string{"What must be", "acknowledgment ", "shall be "},},{words: []string{"Science", "is", "what", "we", "understand", "well", "enough", "to", "explain", "to", "a", "computer.", "Art", "is", "everything", "else", "we", "do"},maxWidth: 20,expect: []string{"Science is what we", "understand well", "enough to explain to", "a computer. Art is", "everything else we", "do "},},{words: []string{"a"},maxWidth: 1,expect: []string{"a"},},{words: []string{"a", "b"},maxWidth: 3,expect: []string{"a b"},},{words: []string{"a", "b", "c"},maxWidth: 5,expect: []string{"a b c"},},{words: []string{"Listen", "to", "many,", "speak", "to", "a", "few."},maxWidth: 6,expect: []string{"Listen", "to ", "many, ", "speak ", "to a", "few. "},},}fmt.Println("方法一:贪心+分段格式化")runTests(testCases, fullJustify)fmt.Println("\n方法二:模拟排版")runTests(testCases, fullJustify2)fmt.Println("\n方法三:递归分行")runTests(testCases, fullJustify3)fmt.Println("\n方法四:预计算空格数组")runTests(testCases, fullJustify4)// 详细输出示例fmt.Println("\n=== 详细输出示例 ===")detailedExample()
}// runTests 运行测试用例
func runTests(testCases []struct {words []stringmaxWidth intexpect []string
}, fn func([]string, int) []string) {passCount := 0for i, tc := range testCases {result := fn(tc.words, tc.maxWidth)status := "✅"// 比较结果if len(result) != len(tc.expect) {status = "❌"} else {for j := range result {if result[j] != tc.expect[j] {status = "❌"break}}}if status == "✅" {passCount++}fmt.Printf(" 测试%d: %s\n", i+1, status)if status == "❌" {fmt.Printf(" 输入: %v, maxWidth=%d\n", tc.words, tc.maxWidth)fmt.Printf(" 输出: %v\n", result)fmt.Printf(" 期望: %v\n", tc.expect)}}fmt.Printf(" 通过: %d/%d\n", passCount, len(testCases))
}// detailedExample 详细输出示例
func detailedExample() {words := []string{"This", "is", "an", "example", "of", "text", "justification."}maxWidth := 16fmt.Printf("输入: words = %v\n", words)fmt.Printf(" maxWidth = %d\n\n", maxWidth)result := fullJustify(words, maxWidth)fmt.Println("输出:")for i, line := range result {// 显示行号和内容fmt.Printf(" 行%d: \"%s\" (长度:%d)\n", i+1, line, len(line))// 显示空格分布spaceCount := 0for _, ch := range line {if ch == ' ' {spaceCount++}}fmt.Printf(" 空格数: %d\n", spaceCount)}// 分析每行的空格分配fmt.Println("\n空格分配分析:")fmt.Println(" 第1行: \"This is an\"")fmt.Println(" 单词: [This, is, an], 长度: 4+2+2=8")fmt.Println(" 总空格: 16-8=8, 间隙: 2")fmt.Println(" 每个间隙: 8/2=4个空格")fmt.Println("\n 第2行: \"example of text\"")fmt.Println(" 单词: [example, of, text], 长度: 7+2+4=13")fmt.Println(" 总空格: 16-13=3, 间隙: 2")fmt.Println(" 基础空格: 3/2=1, 额外: 3%2=1")fmt.Println(" 第1个间隙: 1+1=2, 第2个间隙: 1")fmt.Println("\n 第3行: \"justification. \"")fmt.Println(" 单词: [justification.], 长度: 14")fmt.Println(" 最后一行,左对齐,右侧填充2个空格")
}