LeetCode 第68题:文本左右对齐

87 阅读4分钟

LeetCode 第68题:文本左右对齐

题目描述

给定一个单词数组 words 和一个长度 maxWidth,重新排版单词,使其成为每行恰好有 maxWidth 个字符,且左右两端对齐的文本。

你应该使用"贪心"的方法来放置给定的单词;也就是说,尽可能多地往每行中放置单词。必要时可用空格 ' ' 填充,使得每行恰好有 maxWidth 个字符。

要求尽可能均匀分配单词间的空格数量。如果某一行单词间的空格不能均匀分配,则左侧放置的空格数要多于右侧的空格数。

文本的最后一行应为左对齐,且单词之间不插入额外的空格。

难度

困难

题目链接

点击在LeetCode中查看题目

示例

示例 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 " ]

示例 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

解题思路

分步处理法

这道题的关键是处理好以下几个方面:

  1. 确定每行能放下哪些单词
  2. 计算空格分配
  3. 特殊处理最后一行
  4. 处理只有一个单词的行

具体步骤:

  1. 遍历单词数组,确定每行的单词
  2. 计算单词间需要插入的空格数
  3. 分配空格(需要考虑均匀分配)
  4. 特殊处理最后一行(左对齐)
  5. 将处理好的每行文本添加到结果中

图解思路

算法步骤分析表

步骤操作状态说明
初始收集单词["This", "is", "an"]确定一行的单词
计算空格16-(4+2+2)=8需要8个空格计算总空格数
分配空格8÷2=4每间隔4个空格均匀分配空格
生成行"This is an"完成一行拼接结果

状态/情况分析表

情况输入输出说明
普通行["This", "is", "an"]"This is an"空格均匀分配
最后一行["shall", "be"]"shall be "左对齐
单词行["acknowledgment"]"acknowledgment "右侧补空格

代码实现

C# 实现

public class Solution {
    public IList<string> FullJustify(string[] words, int maxWidth) {
        List<string> result = new List<string>();
        int index = 0;
        
        while (index < words.Length) {
            // 计算当前行可以放多少个单词
            int count = GetWordsCount(words, index, maxWidth);
            
            // 处理当前行
            string line = CreateLine(words, index, count, maxWidth, index + count == words.Length);
            result.Add(line);
            
            index += count;
        }
        
        return result;
    }
    
    private int GetWordsCount(string[] words, int start, int maxWidth) {
        int width = 0;
        int count = 0;
        
        for (int i = start; i < words.Length; i++) {
            // 加上当前单词长度和至少一个空格
            width += words[i].Length + (count > 0 ? 1 : 0);
            if (width > maxWidth) break;
            count++;
        }
        
        return count;
    }
    
    private string CreateLine(string[] words, int start, int count, int maxWidth, bool isLastLine) {
        StringBuilder sb = new StringBuilder();
        
        // 如果是最后一行或只有一个单词,使用左对齐
        if (isLastLine || count == 1) {
            for (int i = 0; i < count; i++) {
                if (i > 0) sb.Append(' ');
                sb.Append(words[start + i]);
            }
            while (sb.Length < maxWidth) sb.Append(' ');
            return sb.ToString();
        }
        
        // 计算空格
        int totalSpaces = maxWidth;
        for (int i = 0; i < count; i++) {
            totalSpaces -= words[start + i].Length;
        }
        
        // 计算每个间隔的空格数
        int gaps = count - 1;
        int spacesPerGap = totalSpaces / gaps;
        int extraSpaces = totalSpaces % gaps;
        
        // 构建当前行
        for (int i = 0; i < count; i++) {
            if (i > 0) {
                int spaces = spacesPerGap + (extraSpaces-- > 0 ? 1 : 0);
                sb.Append(new string(' ', spaces));
            }
            sb.Append(words[start + i]);
        }
        
        return sb.ToString();
    }
}

Python 实现

class Solution:
    def fullJustify(self, words: List[str], maxWidth: int) -> List[str]:
        result = []
        i = 0
        
        while i < len(words):
            # 计算当前行可以放多少个单词
            line_words = []
            line_len = 0
            while i < len(words) and line_len + len(words[i]) + len(line_words) <= maxWidth:
                line_words.append(words[i])
                line_len += len(words[i])
                i += 1
            
            # 处理最后一行或只有一个单词的情况
            if i == len(words) or len(line_words) == 1:
                line = ' '.join(line_words)
                line += ' ' * (maxWidth - len(line))
                result.append(line)
                continue
            
            # 计算空格
            total_spaces = maxWidth - line_len
            gaps = len(line_words) - 1
            spaces_per_gap = total_spaces // gaps
            extra_spaces = total_spaces % gaps
            
            # 构建当前行
            line = ''
            for j in range(len(line_words)):
                line += line_words[j]
                if j < len(line_words) - 1:
                    spaces = spaces_per_gap + (1 if j < extra_spaces else 0)
                    line += ' ' * spaces
            
            result.append(line)
        
        return result

C++ 实现

class Solution {
public:
    vector<string> fullJustify(vector<string>& words, int maxWidth) {
        vector<string> result;
        int i = 0;
        
        while (i < words.size()) {
            // 计算当前行可以放多少个单词
            vector<string> lineWords;
            int lineLen = 0;
            while (i < words.size() && lineLen + words[i].length() + lineWords.size() <= maxWidth) {
                lineWords.push_back(words[i]);
                lineLen += words[i].length();
                i++;
            }
            
            // 处理最后一行或只有一个单词的情况
            if (i == words.size() || lineWords.size() == 1) {
                string line = lineWords[0];
                for (int j = 1; j < lineWords.size(); j++) {
                    line += " " + lineWords[j];
                }
                line += string(maxWidth - line.length(), ' ');
                result.push_back(line);
                continue;
            }
            
            // 计算空格
            int totalSpaces = maxWidth - lineLen;
            int gaps = lineWords.size() - 1;
            int spacesPerGap = totalSpaces / gaps;
            int extraSpaces = totalSpaces % gaps;
            
            // 构建当前行
            string line = lineWords[0];
            for (int j = 1; j < lineWords.size(); j++) {
                int spaces = spacesPerGap + (j <= extraSpaces ? 1 : 0);
                line += string(spaces, ' ') + lineWords[j];
            }
            
            result.push_back(line);
        }
        
        return result;
    }
};

执行结果

  • 执行用时:96 ms
  • 内存消耗:38.1 MB

代码亮点

  1. 🎯 清晰的分步处理逻辑
  2. 💡 巧妙处理空格分配
  3. 🔍 特殊情况处理完善
  4. 🎨 代码结构模块化

常见错误分析

  1. 🚫 空格分配计算错误
  2. 🚫 最后一行处理错误
  3. 🚫 单词行处理错误
  4. 🚫 未考虑边界情况

解法对比

解法时间复杂度空间复杂度优点缺点
分步处理O(n)O(maxWidth)逻辑清晰代码较长
一次遍历O(n)O(maxWidth)代码简短难以维护
递归处理O(n)O(n)结构清晰性能较差

相关题目