【问题标题】:Finding sub matrix of a given matrix查找给定矩阵的子矩阵
【发布时间】:2012-04-10 17:24:18
【问题描述】:

我正在尝试编写一种算法来在给定的子矩阵中查找子矩阵。为了解决这个问题,我编写了以下代码:

public class SubMatTry {

/**
 * @param args
 */
public static void main(String[] args) {
    // TODO Auto-generated method stub
    int a[][] = { { 2, 3, 5, 7 }, { 5, 8, 3, 5 }, { 7, 6, 9, 2 },
            { 3, 8, 5, 9 } };
    int b[][] = { { 9, 2 }, { 5, 9 } };
    int k = 0;
    int l = 0;
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            System.out.println("Element of a= " + a[i][j]);
            if (b[k][l] == a[i][j]) {
                System.out.println(b[k][l] + " = " + a[i][j]);
                if (b[k][l + 1] == a[i][j + 1]) {
                    System.out.println(b[k][l + 1] + " = " + a[i][j + 1]);
                    if (b[k + 1][l] == a[i + 1][j]) {
                        System.out.println(b[k + 1][l] + " = "
                                + a[i + 1][j]);
                        if (b[k + 1][l + 1] == a[i + 1][j + 1]) {
                            System.out.println(b[k + 1][l + 1] + " = "
                                    + a[i + 1][j + 1]);
                            System.out.println("Array found at" + i + " ,"
                                    + j);
                            System.exit(0);
                        }
                    }
                }
            }
        }

    }

}}

此代码运行良好,但我不确定它是问题的确切解决方案还是只是一种解决方法。请提供您的专家 cmet。提前致谢。

【问题讨论】:

  • 感谢 tmikulcek 抽出时间回答我的问题。您提供的链接不能解决我关注的问题。但是,是的,这对我更接近解决我的问题很有帮助。

标签: java multidimensional-array submatrix


【解决方案1】:

该算法针对 4×4 矩阵和 2×2 子矩阵进行了硬编码。否则它看起来就像一个蛮力算法。

我会这样表达:

outerRow:
for (int or = 0; or <= a.length - b.length; or++) {
    outerCol:
    for (int oc = 0; oc <= a[or].length - b[0].length; oc++) {
        for (int ir = 0; ir < b.length; ir++)
            for (int ic = 0; ic < b[ir].length; ic++)
                if (a[or + ir][oc + ic] != b[ir][ic])
                    continue outerCol;
        System.out.println("Submatrix found at row " + or + ", col " + oc);
        break outerRow;
    }
}

如果你想要更高效的东西,我建议你把它们弄平,像这样:

{ 2,3,5,7, 5,8,3,5, 7,6,9,2, 3,8,5,9 }

并在此序列中搜索以下模式:

{ 9,2, _, _, 5, 9}

使用标准的查找子字符串技术,例如 Aho-CorasickKnuth-Morris-Pratt algorithm。 (请注意,您必须跳过一些索引以避免在序列中间有新行时出现误报。)

【讨论】:

  • 我从你的帖子中了解到的是解决子矩阵问题首先将二维数组转换为一维数组,然后在给定数组中查找给定模式。
  • 是的。如果您在展平的数组中找到 9,2,something,something,59,则您已找到匹配的子矩阵。
  • 这是不正确的。考虑矩阵 {[4, 9, 2], [3, 2, 1]} 和子矩阵 {[9, 2], [2, 1]}。子矩阵存在于原始矩阵中,但 [9, 2, 2, 1] 不存在于 [4, 9, 2, 3, 2, 1] 中。
  • 对,但是您必须寻找 [9, 2, _, 2, 1] 因为您有 3 列并且正在寻找 2 列的子矩阵。
  • @aioobe 请查看我在下面发布的答案,这是您提出的策略的实施。
【解决方案2】:

首先,i 和 j 不应该迭代到 3(如果你在 a[3][3] 你知道它不能是一个子矩阵的开始,因为基本上你在结束矩阵)。

其次,不要使用固定数字,例如 4 - 使用 a.length 代替(这为您提供数组的长度 a - 列数,而 a[0].length 将为您提供第一列的长度 - 有效, 行数)。

第三,我会将四重 if(原文如此)更改为双重 for,迭代 k 和 l,如下所示:

for (int i = 0; i < a.length - b.length + 1; i++) {
        for (int j = 0; j < a[0].length - b[0].length + 1; j++) {
            boolean submatrix = true; // at start we assume we have a submatrix
            for (int k = 0; k < b.length; ++k) {
                for (int l = 0; l < b[0].length; ++l) {
                    if (a[i + k][j + l] == b[k][l]) {
                        System.out.println("a[" + (i + k) + "][" + (j + l) + "] = b[" + k + "][" + l + "]");
                    } else {
                        submatrix = false; // we found inequality, so it's not a submatrix
                    }
                }
            }
            if (submatrix) {
                System.out.println("Found subatrix at " + i + "," + j + ".");
            }
        }
    }

(不确定它是否真的有效,没有通过编译器;))

除此之外,如果您使用 java,您应该尝试习惯对象、类和方法(Matrix 类和 boolean isSubmatrix(Matrix b) 方法) - 但对于初学者来说应该这样做。

希望我的回答有帮助。

【讨论】:

  • 如果我使用 if(a[i][j] == b[k][l]) 而不是 if(a[i][j] 来执行您的代码== n[k][l]) 我得到的输出是:a[0][0] = b[0][1] a[1][0] = b[1][0]
  • 您当然是对的,忘记了两件事,抱歉笨拙的回答;)。我已经编辑了代码,这次我将它通过编译器并且它可以工作,在 2,2 处找到一个子矩阵。
【解决方案3】:

以下是我根据@aioobe 描述的策略编写的解决方案

public static boolean matrixContainsPattern(int[][] data, int[][] pattern) {
    int[] flatData = flattenMatrix(data);
    int[] flatPattern = flattenMatrix(pattern);

    //If the # of rows of data is less than the rows of pattern, we have a problem since we can match at most only a partial amount of the pattern into data
    if (flatData.length < flatPattern.length) {
        throw new IllegalArgumentException();
    }

    int dataRowLen = data[0].length;
    int patternRowLen = pattern[0].length;
    for (int i = 0; i < flatData.length - flatPattern.length + 1; i++) {
        //We found a potential match for the pattern
        if (flatData[i] == flatPattern[0]) {
            //k can keep track of indexes inside flatData
            int k = i;
            //l can keep track of indexes inside flatPattern
            int l = 0;
            //dataRowOffset will help us keep track of WHERE we found a match in flatPatterns' imaginary rows
            int dataRowOffset = (i % dataRowLen);
            //count to keep track of when we've reached the end of an imaginary row in data
            int count = 1;
            boolean patternFound = true;
            while (k < flatData.length && l < flatPattern.length) {
                if (flatData[k] != flatPattern[l]) {
                    patternFound = false;
                    break;
                }
                //if we reached the end of an imaginary row, we need to skip our pointer k to the next rows offset location
                //we also need to reset count to the offset, so we can again find the end of this new imaginary row
                if (count == patternRowLen) {
                    //To get to the position in the next row of where we first found our match, we add to k: the length of whats remaining in our current row,
                    //plus the offset from where we first found in the match in the current row
                    if (dataRowLen == patternRowLen) {
                        k++;
                    } else {
                        k += (dataRowLen - patternRowLen) + dataRowOffset;
                    }
                    count = 1;
                } else {
                    k++;
                    count++;
                }
                l++;
            }
            if (patternFound) {
                return true;
            }
        }
    }
    return false;
}

而将矩阵展平为数组的方法如下:

private static int[] flattenMatrix(int[][] matrix) {
        if (matrix == null || matrix[0] == null || matrix[0].length < 1) {
            throw new IllegalArgumentException();
        }
        int[] flattened = new int[matrix.length * matrix[0].length];

        int k = 0;
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                flattened[k] = matrix[i][j];
                k++;
            }
        }
        return flattened;
    }

【讨论】:

    【解决方案4】:

    这里将两个矩阵作为输入 n,m 用于更大的矩阵,r,c 用于要找到的子矩阵:-

    int ans=0,f=0;
        for(int i=0;i<n-r+1;i++)
        {
            for(int j=0;j<m-c+1;j++)
            {
                f=0;
                for(int p=0;p<r;p++)
                {
                    for(int q=0;q<c;q++)
                    {
                        if(a[i+p][j+q]!=b[p][q])
                        {
                            f=1;
                            break;
                        }
                    }
                }
                if(f==0)
                {
                    ans=1;
                    break;
                }
            }
            if(ans==1)
            break;
        }
         cout<<(ans?"YES":"NO")<<'\n';
    

    【讨论】: