题目描述:
给定一个单词数组和一个长度 maxWidth,重新排版单词,使其成为每行恰好有 maxWidth 个字符,且左右两端对齐的文本。 你应该使用“贪心算法”来放置给定的单词;也就是说,尽可能多地往每行中放置单词。必要时可用空格 ' ' 填充,使得每行恰好有 maxWidth 个字符。 要求尽可能均匀分配单词间的空格数量。如果某一行单词间的空格不能均匀分配,则左侧放置的空格数要多于右侧的空格数。 文本的最后一行应为左对齐,且单词之间不插入额外的空格。 说明: 单词是指由非空格字符组成的字符序列。 每个单词的长度大于 0,小于等于 maxWidth。 输入单词数组 words 至少包含一个单词。 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/text-justification 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
示例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 " ]
题目难度:困难
分析:
题目意思比较好理解,无非就是单词+空格+单词+空格...然后计算所有长度,取每行的最大长度即可。难在确定了每行应放的单词后,剩余的空格怎么分配?我的思路就是:当我确定了每行的单词个数以及单词的长度和后,用需要补全的空格数以及单词间的间隙数来计算最后的每个间隙需要补全的空格数。代码如下:
java:
class Solution { public List<String> fullJustify(String[] words, int maxWidth) { // 用来存储最终结果 List<String> res = new ArrayList<>(); // 用来存储临时单词,即每一行拼接之前的所有单词 List<String> tempList = new ArrayList<>(); // 上面临时单词的长度和 int currentLength = 0; // 用来拼接临时单词 StringBuilder currentStr = new StringBuilder(); for (int i = 0; i < words.length; i++) { // 临时单词 String tempStr = words[i]; // 计算拼接完单词后的最小长度是否会超出maxWidth,如果是2个单词即长度等于2个单词的长度加1个空格, // 3个单词就是3个单词长度加2个空格,以此类推,此时当前单词还没有放入tempList,所以size就是空格数 int tempCurrentLength = currentLength + tempStr.length() + tempList.size(); if (tempCurrentLength == maxWidth) { // 如果正好相等,那么按照一个单词一个空格添加到currentStr即可 tempList.add(tempStr); for (String s : tempList) { currentStr.append(s); currentStr.append(" "); } // 这里是去掉上方循环后多余的一个空格 currentStr = new StringBuilder(currentStr.substring(0, currentStr.length() - 1)); } else if (tempCurrentLength > maxWidth) { // 这是最复杂的情况,下一个单词加入会超过maxWidth的值 // 先计算一共需要多少个空格 int spaceCount = maxWidth - currentLength; // 这里是间隙数,就是3个单词中间有两个间隙,4个单词有3个间隙,因为尾部是不允许有空格的 int space = tempList.size() - 1; // 这里是单个间隙需要多少空格 int singleSpaceCount = 0; // 这里是剩余的空格,因为可能会出现不平均的情况,比如5个空格,3个间隙,那么应该是221分配 int remainderCount = 0; // 因为会出现一行就一个长单词的情况,所以间隙数可能为0 if (space != 0) { // 还用5个空格,3个间隙举例,那么singleSpaceCount = 1,remainderCount = 2 // 代表3个间隙每个间隙一个空格,还剩余2个空格,至于这2个怎么分配,下面会说到 singleSpaceCount = spaceCount / space; remainderCount = spaceCount % space; } for (int j = 0; j < tempList.size(); j++) { String s = tempList.get(j); currentStr.append(s); // 添加完单词后,循环插入空格,注意:最后一个单词后面不需要空格 for (int m = 0; m < singleSpaceCount && j != tempList.size() - 1; m++) { currentStr.append(" "); // 这里就是remainderCount剩余空格的分配,因为题目有说到:左侧空格应大于右侧 // 所以当remainderCount>0时,每次多分配一个,即可处理完所有的remainderCount if (remainderCount > 0) { currentStr.append(" "); remainderCount--; } } } // 这里是处理前面说到的一个长单词单独占据一行的情况,需要在最后补全空格 if (currentStr.length() < maxWidth) { int n = maxWidth - currentStr.length(); for (int j = 0; j < n; j++) { currentStr.append(" "); } } // 这个i--是因为当前单词长度和大于maxWidth了,没有进行拼接,所以需要回退继续下一轮 i--; } else { // 最后只剩小于的情况,那么长度累加,单词放入临时集合存储,继续循环 currentLength += tempStr.length(); tempList.add(tempStr); // 此时没有达到>或者=的情况,所以用continue结束循环 continue; } // 经过前面判断,如果此时是>或者=的情况,那么直接把上方处理好的currentStr添加到res, // 然后清空currentStr、tempList、currentLength res.add(currentStr.toString()); currentStr = new StringBuilder(); tempList.clear(); currentLength = 0; } // 还有种情况就是最后单词不够一行,那么此时tempList里必然还存有元素,单独作为一行即可 if (tempList.size() > 0) { for (String s : tempList) { currentStr.append(s); currentStr.append(" "); } // 最后补全空格,添加到res即可 if (currentStr.length() < maxWidth) { int n = maxWidth - currentStr.length(); for (int i = 0; i < n; i++) { currentStr.append(" "); } } res.add(currentStr.toString()); } return res; } }
总结:
题目在困难级别里应该算作较为简单的了,思路也比较清晰,难在代码的书写篇幅过大,需要考虑的情况也较多,有debug调试会轻松许多。