LeetCode破解之文本左右对齐

193 阅读2分钟

「这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战

题目描述

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

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

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

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

说明:

  • 单词是指由非空格字符组成的字符序列。
  • 每个单词的长度大于 0,小于等于 maxWidth
  • 输入单词数组 words 至少包含一个单词。

示例1:

输入: words = ["This", "is", "an", "example", "of", "text", "justification."] maxWidth = 16 输出: [    "This    is    an",    "example  of text",    "justification.  " ]

解法:滑动窗口

分析:核心就是每组可以组成一行的 i - j 个窗口,然后在每个窗口内计算出空格space的数量,依次分配给各个字符串。

具体步骤

设每一缓存行,一个数组来缓存单词的索引,一个长度变量来缓存当前已占用长度,一个长度变量缓存总单词长度。

不均衡空格的分配思路:

  • 如总数10个空格分给4个位置,算出平均每个each = 10/4 =2
  • 由于10%4!=0,第一个位置的空格数为each +1,然后剩下7个分3个位置
  • 7%3!=0,第二个位置空格数为each+1,然后剩下4个分两个位置
  • 最后得出空格块分别为3,3,2,2
class Solution {
    //文本居中
    private String textAlignCenter(String[] words,int start,int end,int lineLen,int maxWidth){
        int space = maxWidth - lineLen,gap = end - start;
        StringBuilder sb = new StringBuilder();

        if(gap == 0){
            sb.append(words[start]);
            while(sb.length() < maxWidth) sb.append(' ');
            return sb.toString();
        }

        int mod = space % gap,div = space / gap;
        sb.append(words[start]);

        for(int i = start + 1;i <= end;i++){
            if(mod-- > 0) 
                sb.append(' ');
            for(int j = 0;j < div;j++)
                sb.append(' ');
            sb.append(words[i]);
        }

        return sb.toString();
    }
    //文本左对齐
    private String textLeftJustify(String[] words,int start,int end,int lineLen,int maxWidth){
        StringBuilder sb = new StringBuilder();
        sb.append(words[start]);

        for(int i = start + 1;i <= end;i++){
            sb.append(' ').append(words[i]);
        }

        while(sb.length() < maxWidth)
            sb.append(' ');

        return sb.toString();
    }

    public List<String> fullJustify(String[] words, int maxWidth) {
        //lineHead表示每行第一个单词索引,lineTail表示每行最后一个单词索引,lineLen表示每行单词总长度(不计空格)
        int lineHead = 0,lineTail = 0,lineLen = 0;
        int n = words.length;
        List<String> res = new ArrayList<>();

        for(int i = 0;i < n;i++){
            String word = words[i];
            if(lineLen + word.length() + i - lineHead > maxWidth){
                lineTail = i - 1;
                res.add(textAlignCenter(words,lineHead,lineTail,lineLen,maxWidth));
                lineHead = i--;
                lineLen = -word.length();
            }
            lineLen += word.length();
        }

        res.add(textLeftJustify(words,lineHead,n - 1,lineLen,maxWidth));
        return res;
    }
}