[LeetCode#68] Text Justification

本文探讨了如何使用贪心算法高效地解决文本对齐问题,确保每行字符数固定,并合理分配额外空格。通过实例分析,详细阐述了算法步骤及常见错误解决方案,旨在提供清晰、优雅的代码实现。

Problem:

Given an array of words and a length L, format the text such that each line has exactly L characters and is fully (left and right) justified.

You should pack your words in a greedy approach; that is, pack as many words as you can in each line. Pad extra spaces ' ' when necessary so that each line has exactly Lcharacters.

Extra spaces between words should be distributed as evenly as possible. If the number of spaces on a line do not divide evenly between words, the empty slots on the left will be assigned more spaces than the slots on the right.

For the last line of text, it should be left justified and no extra space is inserted between words.

For example,
words: ["This", "is", "an", "example", "of", "text", "justification."]
L: 16.

Return the formatted lines as:

[
   "This    is    an",
   "example  of text",
   "justification.  "
]

 

Note: Each word is guaranteed not to exceed L in length.

Analysis:

There is no algorithm test behind this problem. It just tests your coding skills in implementation.
Beief idea:
Step 1.
Scan the word array, and once you got enough words for the for a way, you use those words to construct a formatted row.
During the scan, we only know the end word of current window after we scan the word after the window. (Which means our last row have to word to invoke the condition).

Step 1.1
To convert an array of words into a a valid row, we need to take care following conditions, 
c1. there must be at least one space between two consecutive words.
c2. If we have extra place, we should divide those extra place among first words. 

Wrong solution 1:
public class Solution {
    public List<String> fullJustify(String[] words, int maxWidth) {
        List<String> ret = new ArrayList<String> ();
        if (words == null || words.length == 0)
            return ret;
        Queue<String> queue = new LinkedList<String> ();
        int word_count = 0;
        int len = 0;
        for (String word : words) {
            if (queue.isEmpty()) {
                queue.offer(word);
                word_count++;
                len += word.length();
            } else{
                if (len + 1 + word.length() <= maxWidth) {
                    len++;
                    word_count++;
                    len += word.length();
                } else{
                    adjustRow(queue, maxWidth, word_count, len, ret);
                    len = 0;
                    word_count = 0;
                    queue.offer(word);
                }
            }
        }
        String last_row = "";
        while (!queue.isEmpty()) {
            String word = queue.poll();
            if (!last_row.equals(""))
                last_row += " ";
            last_row += word;
        }
        return ret;
    }
    
    
    private void adjustRow(Queue<String> queue, int maxWidth, int word_count, int len, List<String> ret) {
        if (queue.isEmpty())
            return;
        String row_str = queue.poll();
        if (word_count == 1) {
           ret.add(row_str);
           return;
        }
        //len should be the total length of word, not include " " string
        int avg_space = (maxWidth - len) / (word_count - 1);
        int extra_space = (maxWidth - len) % (word_count - 1);
        while (!queue.isEmpty()) {
            String word = queue.poll();
            int count = avg_space;
            while (avg_space > count) {
                row_str += " ";
                count++;
            }
            if (extra_space != 0) {
                row_str += " ";
                extra_space--;
            }
            row_str += word; 
        }
        ret.add(row_str);
    }
}


Input:
[""]
0
Output:
[]
Expected:
[""]

Mistake analysis:
Even though the basic idea is right. The above implementation is really really ugly.
The most problemtic part comes out from trying to record the word's number and word's length in a window, which would definitely incure a lot of problems during those "if - else codition". 
-----------------------------------------------------------
int word_count = 0;
        int len = 0;
        for (String word : words) {
            if (queue.isEmpty()) {
                queue.offer(word);
                word_count++;
                len += word.length();
            } else{
                if (len + 1 + word.length() <= maxWidth) {
                    len++;
                    word_count++;
                    len += word.length();
                } else{
                    adjustRow(queue, maxWidth, word_count, len, ret);
                    len = 0;
                    word_count = 0;
                    queue.offer(word);
                }
            }
        }

------------------------------------------------------------
For interview, you should never try to implement such complex mechanism. If you just redo the caculation of "word_count" and "len" at the adjust Row. The code could be much elegant! And thus save you a lot of energy. Don't worry about a little increse in time complexity. Just blindly save and improvement could trap you into a pitfall.


Wrong solution 2:
public class Solution {
   public List<String> fullJustify(String[] words, int maxWidth) {
        List<String> ret = new ArrayList<String> ();
        if (words == null || words.length == 0)
            return ret;
        Queue<String> queue = new LinkedList<String> ();
        int len = 0;
        for (String word : words) {
            if (queue.isEmpty()) {
                queue.offer(word);
                len += word.length();
            } else{
                if (len + 1 + word.length() <= maxWidth) {
                    len++;
                    len += word.length();
                } else{
                    adjustRow(queue, maxWidth, ret);
                    len = 0;
                }
                queue.offer(word);
            }
        }
        String last_row = "";
        while (!queue.isEmpty()) {
            String word = queue.poll();
            if (!last_row.equals(""))
                last_row += " ";
            last_row += word;
        }
        while (last_row.length() < maxWidth) {
            last_row += " ";   
        }
        ret.add(last_row);
        return ret;
    }
    
    
    private void adjustRow(Queue<String> queue, int maxWidth, List<String> ret) {
        if (queue.isEmpty())
            return;
        int len = 0;
        int word_count = queue.size();
        for (String word : queue) {
            len += word.length();
        }
        String row_str = queue.poll();
        if (word_count == 1) {
           ret.add(row_str);
           return;
        }
        int avg_space = (maxWidth - len) / (word_count - 1);
        int extra_space = (maxWidth - len) % (word_count - 1);
        while (!queue.isEmpty()) {
            String word = queue.poll();
            int count = 0;
            while (avg_space > count) {
                row_str += " ";
                count++;
            }
            if (extra_space != 0) {
                row_str += " ";
                extra_space--;
            }
            row_str += word; 
        }
        ret.add(row_str);
    }
}

Input:
["Listen","to","many,","speak","to","a","few."]
6
Output:
["Listen","tomany,","speaktoa","few.  "]
Expected:
["Listen","to    ","many, ","speak ","to   a","few.  "]

Mistake analysis:
Even we have managed to to change to code into a more elegant way. I still have made a big misktake in to above code. Which is quite common when use "after-ward" identification of a window. 
for (String word : words) {
    if (queue.isEmpty()) {
        queue.offer(word);
        len += word.length();
    } else{
        if (len + 1 + word.length() <= maxWidth) {
            len++;
            len += word.length();
        } else{
            adjustRow(queue, maxWidth, ret);
            len = 0;
        }
        queue.offer(word);
    }
}

Note:
"after-ward detection" is very very dangerous!!! Especially you even matain a window for it, which means the stop condition is not the last element. You should pay close attention for following things:
1. We only know the end of a window at the next element after the window. You should do a good design over : "When is the window valid? "
For this problem: the words in the window does not exceed maxWidth, including the concern of "at least one space between two word". 
                if (queue.isEmpty()) { //the first word does not need a space before it.
                    queue.offer(word);
                    len += word.length();
                } else{
                    if (len + 1 + word.length() <= maxWidth) { //test if the window was exceeded?
                        len++;
                        queue.offer(word);
                        len += word.length();
                    } else{ //if exceeded, we apparently need to open a new window
                        adjustRow(queue, maxWidth, ret);
                        queue.offer(word);
                        len = word.length();
                    }
                }

2. Most dangerous part: 
Iff a window was exceeded, we should prepare new window!!!!
But don't forget to include current word into the new window, and all its information!!!

Buggy codes:
if (len + 1 + word.length() <= maxWidth) {
    len++;
    len += word.length();
} else{
    adjustRow(queue, maxWidth, ret);
    len = 0;
}
queue.offer(word);

The above code has following problems:
mistake 1: forget to include the violated word's length into next window.
mistake 2: try to extract common code "queue.offer(word)" out!!! Which is very dangerous for initial implementation.


3. Don't forget the last window for the last row.
            String last_row = "";
            while (!queue.isEmpty()) {
                String word = queue.poll();
                if (!last_row.equals(""))
                    last_row += " ";
                last_row += word;
            }
            while (last_row.length() < maxWidth) { //don't forget to add space after the last word(when there is only one word)
                last_row += " ";   
            }
            ret.add(last_row);

Solution:

public class Solution {
        public List<String> fullJustify(String[] words, int maxWidth) {
            List<String> ret = new ArrayList<String> ();
            if (words == null || words.length == 0)
                return ret;
            Queue<String> queue = new LinkedList<String> ();
            int len = 0;
            for (String word : words) {
                if (queue.isEmpty()) {
                    queue.offer(word);
                    len += word.length();
                } else{
                    if (len + 1 + word.length() <= maxWidth) {
                        len++;
                        queue.offer(word);
                        len += word.length();
                    } else{
                        adjustRow(queue, maxWidth, ret);
                        queue.offer(word);
                        len = word.length();
                    }
                }
            }
            String last_row = "";
            while (!queue.isEmpty()) {
                String word = queue.poll();
                if (!last_row.equals(""))
                    last_row += " ";
                last_row += word;
            }
            while (last_row.length() < maxWidth) {
                last_row += " ";   
            }
            ret.add(last_row);
            return ret;
        }
        
        
       private void adjustRow(Queue<String> queue, int maxWidth, List<String> ret) {
            if (queue.isEmpty())
                return;
            int len = 0;
            int word_count = queue.size();
            for (String word : queue) {
                len += word.length();
            }
            String row_str = queue.poll();
            int avg_space = 0;
            int extra_space = 0;
            if (word_count > 1) {
                avg_space = (maxWidth - len) / (word_count - 1);
                extra_space = (maxWidth - len) % (word_count - 1);
            }
            while (!queue.isEmpty()) {
                String word = queue.poll();
                int count = 0;
                while (avg_space > count) {
                    row_str += " ";
                    count++;
                }
                if (extra_space != 0) {
                    row_str += " ";
                    extra_space--;
                }
                row_str += word; 
            }
            while (row_str.length() < maxWidth)
                row_str += " ";
            ret.add(row_str);
        }
}

 

转载于:https://www.cnblogs.com/airwindow/p/4840373.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值