文本对齐算法

时间:2018-06-25 09:03:10

标签: java recursion dynamic-programming

这是DP中一个非常著名的问题,有人可以帮助可视化它的递归部分。如何生成排列或组合。

问题参考。 https://www.geeksforgeeks.org/dynamic-programming-set-18-word-wrap/

1 个答案:

答案 0 :(得分:0)

假定最大行宽为L(证明文本T合理)的想法是考虑文本的所有后缀(为了精确地形成后缀,请考虑单词而不是字符)。 动态编程不过是“谨慎的蛮力”。 如果您考虑采用蛮力方法,则需要执行以下操作。

  1. 考虑在第一行中放入1,2,.. n个单词。
  2. 对于在情况1中描述的每种情况(假设i个单词放在第1行中),请考虑在第2行中放置1,2,.. n -i个单词,然后在第3行中剩余单词的情况,依此类推。 。

相反,我们只考虑问题,以找出将单词放在行首的成本。 通常,我们可以将DP(i)定义为将第(i-1)个单词视为行的开头的代价。

如何为DP(i)形成递归关系?

如果第j个单词是下一行的开头,则当前行将包含单词[i:j)(j排他),而第j个单词作为下一行的开头的开销将为DP(j)。 因此,DP(i)= DP(j)+在当前行中放置单词[i:j)的成本 由于我们希望使总成本最小化,因此可以定义DP(i)。

重复关系:

  

DP(i)=最小{DP(j)+在当前行中放置单词[i:j的成本}   对于[i + 1,n]中的所有j

注意j = n表示下一行没有剩余的单词。

基本情况:DP(n)= 0 =>此时,没有可写的字了。

总结:

  1. 子问题:后缀,单词[:i]
  2. 猜:从下一行开始,选择项n-i-> O(n)
  3. 重复发生:DP(i)=最小值{DP(j)+在当前行中放置单词[i:j)的成本} 如果我们使用备忘录,则花括号内的表达式应该花费O(1)时间,并且循环运行O(n)次(选择次数#)。 i从n变化到0 =>因此总复杂度降低到O(n ^ 2)。

现在,即使我们得出证明文本合理的最低成本,我们也需要通过跟踪上面表达式中选择为最小值的j值来解决原始问题,以便以后可以使用相同的值进行打印找出合理的文字。这个想法是保留父指针。

希望这可以帮助您了解解决方案。以下是上述想法的简单实现。

 public class TextJustify {
    class IntPair {
        //The cost or badness
        final int x;

        //The index of word at the beginning of a line
        final int y;
        IntPair(int x, int y) {this.x=x;this.y=y;}
    }
    public List<String> fullJustify(String[] words, int L) {
        IntPair[] memo = new IntPair[words.length + 1];

        //Base case
        memo[words.length] = new IntPair(0, 0);


        for(int i = words.length - 1; i >= 0; i--) {
            int score = Integer.MAX_VALUE;
            int nextLineIndex = i + 1;
            for(int j = i + 1; j <= words.length; j++) {
                int badness = calcBadness(words, i, j, L);
                if(badness < 0 || badness == Integer.MAX_VALUE) break;
                int currScore = badness + memo[j].x;
                if(currScore < 0 || currScore == Integer.MAX_VALUE) break;
                if(score > currScore) {
                    score = currScore;
                    nextLineIndex = j;
                }
            }
            memo[i] = new IntPair(score, nextLineIndex);
        }

        List<String> result = new ArrayList<>();
        int i = 0;
        while(i < words.length) {
            String line = getLine(words, i, memo[i].y);
            result.add(line);
            i = memo[i].y;
        }
        return result;
    }

    private int calcBadness(String[] words, int start, int end, int width) {
        int length = 0;
        for(int i = start; i < end; i++) {
            length += words[i].length();
            if(length > width) return Integer.MAX_VALUE;
            length++;
        }
        length--;
        int temp = width - length;
        return temp * temp;
    }


    private String getLine(String[] words, int start, int end) {
        StringBuilder sb = new StringBuilder();
        for(int i = start; i < end - 1; i++) {
            sb.append(words[i] + " ");
        }
        sb.append(words[end - 1]);

        return sb.toString();
    }
  }