【问题标题】:Dynamic programming or backtracking?动态规划还是回溯?
【发布时间】:2013-05-03 18:51:12
【问题描述】:

我的朋友给了我以下问题:

Input: A matrix of letters and a word.
Output: The frequency of the word in the matrix assuming
  you can move left, right, up and down in the matrix to form the word.

例如:

Input:
S E X Y
A S E A
A A X A
A A Y A
And word is SEXY.

Output:
4 (four times in matrix of letters)

这是我解决问题的代码:

package backtracking;

public class CountFrequency {
    private char[][] matrixOfLetter;
    private String word;
    private int n, m;
    private int lengthOfWord;
    private int[][] matrixCountFrequency;

    public CountFrequency(int n, int m, String word) {
        matrixOfLetter = new char[n][m];
        this.word = word;
        this.n = n;
        this.m = m;
        this.lengthOfWord = word.length();

        matrixCountFrequency = new int[n][m];
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < n; ++j)
                matrixCountFrequency[i][j] = 0;
    }

    public static void main(String[] args) {
        CountFrequency countFrequency = new CountFrequency(4, 4, "SEXY");

        countFrequency.addMatrixOfLetter(0, 0, 'S');
        countFrequency.addMatrixOfLetter(0, 1, 'E');
        countFrequency.addMatrixOfLetter(0, 2, 'X');
        countFrequency.addMatrixOfLetter(0, 3, 'Y');
        countFrequency.addMatrixOfLetter(1, 0, 'A');
        countFrequency.addMatrixOfLetter(1, 1, 'S');
        countFrequency.addMatrixOfLetter(1, 2, 'E');
        countFrequency.addMatrixOfLetter(1, 3, 'A');
        countFrequency.addMatrixOfLetter(2, 0, 'A');
        countFrequency.addMatrixOfLetter(2, 1, 'A');
        countFrequency.addMatrixOfLetter(2, 2, 'X');
        countFrequency.addMatrixOfLetter(2, 3, 'A');
        countFrequency.addMatrixOfLetter(3, 0, 'A');
        countFrequency.addMatrixOfLetter(3, 1, 'A');
        countFrequency.addMatrixOfLetter(3, 2, 'Y');
        countFrequency.addMatrixOfLetter(3, 3, 'A');

        countFrequency.process();
        countFrequency.printResult();
    }

    public void addMatrixOfLetter(int i, int j, char c) {
        matrixOfLetter[i][j] = c;
    }

    public void process() {
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j) {
                if (word.indexOf(matrixOfLetter[i][j]) == -1) {
                    matrixCountFrequency[i][j] = -1;
                    continue;
                }
                if (matrixOfLetter[i][j] == word.charAt(lengthOfWord - 1))
                    processWithLastChar(lengthOfWord - 1, i, j);
            }
    }

    public void processWithLastChar(int indexOfWord, int row, int col) {
        matrixCountFrequency[row][col] += 1;
        if (indexOfWord == 0)
            return;
        else {
            if (row - 1 >= 0) {
                if (matrixOfLetter[row - 1][col] == word
                        .charAt(indexOfWord - 1))
                    processWithLastChar(indexOfWord - 1, row - 1, col);
            }

            if (row + 1 < lengthOfWord) {
                if (matrixOfLetter[row + 1][col] == word
                        .charAt(indexOfWord - 1))
                    processWithLastChar(indexOfWord - 1, row + 1, col);
            }

            if (col - 1 >= 0) {
                if (matrixOfLetter[row][col - 1] == word
                        .charAt(indexOfWord - 1))
                    processWithLastChar(indexOfWord - 1, row, col - 1);
            }

            if (col + 1 < lengthOfWord) {
                if (matrixOfLetter[row][col + 1] == word
                        .charAt(indexOfWord - 1))
                    processWithLastChar(indexOfWord - 1, row, col + 1);
            }
        }
    }

    public void printResult() {
        int count = 0;
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j) {
                if (word.charAt(0) == matrixOfLetter[i][j])
                    count += matrixCountFrequency[i][j];
            }

        System.out.println("Frequency is : " + count);
    }
}

我使用了回溯算法,但我只在看到单词的最后一个字母时回溯,并在看到单词中最右边的字母时再次回溯。

我使用计数器矩阵来计算字母的计数频率。

这个问题可以用动态规划算法解决吗?

或者有什么更好的主意?

【问题讨论】:

    标签: algorithm dynamic-programming backtracking


    【解决方案1】:

    可以用动态规划来解决,我认为这是最容易理解的解决方案。

    为您的矩阵创建一个平行的 3 维矩阵。如果字母矩阵的尺寸为nxm,并且您搜索的单词是L,那么您将创建矩阵dp[n][m][L]。在dp[i][j][k] 中,您存储了您找到的将字母initial[i][j] 用作您单词的第kth 字母的方式。

    您有dp[i][j][k] = sum(dp[i+delta1][j + delta2][k + 1]),其中{delta1, delta2} in {{0, 1},{0, -1}, {1, 0}, {-1, 0}}。递归的底部是delta[i][j][L - 1] = (initial[i][j] == word[L - 1])

    如果你对所有可能的 i 和 j 求和 dp[i][j][l - 1],就会给出最终结果。

    希望这对您有所帮助。

    编辑

    我不得不承认我在最初的解决方案中做了一个愚蠢的提议。我提出的动态解决方案没有考虑到我使用了哪些字母。因此对于矩阵

    XXXX
    XABX
    XXXX
    

    字符串 ABAB 我的算法将返回一个计数 - 从 A 开始到 B,然后返回到 A 再返回到 B。这对于您的需要可能是错误的。

    遗憾地跟踪您已经访问过的内容在动态方法中并不简单,现在我开始认为回溯更适合您的问题。

    顺便说一句,您的解决方案中也没有考虑到这一点,但是跟踪您在回溯期间访问的内容要容易得多。我也认为回溯在内存和性能方面会更有效。

    【讨论】:

    • 非常感谢!我想通过动态编程解决问题的 3 维矩阵,但我无法实现。
    • @TrungHuynh 我已经纠正了我的答案,遗憾的是,证明我错了。请看一下。
    • 我认为回溯的复杂度是 O(mn),和动态规划一样。实际上,我可以通过我访问的标记元素的一些标志变量来使其更快。我会仔细看看你的解决方案。
    • @TrungHuynh 如果您不需要重复字母,则回溯的复杂性会更好。实际复杂度是 O(m * n * 3^(mn)) 最坏情况。而 dyp 解决方案会更糟。但是,如果您可以重复它们,则动态解决方案将具有 O(mnl) 复杂性:)
    • @TrungHuynh 我可以为此做动态解决方案,没有问题。问题是状态非常复杂——它必须携带所有已经使用过的字母。使用这种解决方案,在处理过程中不会多次访问任何状态。这使 dp 毫无意义。这就是为什么我告诉你回溯会更有效率。
    猜你喜欢
    • 2014-05-20
    • 2014-01-13
    • 2011-04-05
    • 2016-04-17
    • 2015-11-09
    • 2019-08-21
    • 2013-12-12
    • 2020-05-20
    • 2012-12-11
    相关资源
    最近更新 更多