【问题标题】:matrix maximum diagonal pattern match矩阵最大对角模式匹配
【发布时间】:2022-01-16 11:00:46
【问题描述】:

我在进行矩阵模式匹配技术面试时遇到了一个问题。我能够使用蛮力解决问题,但想知道是否有更有效的解决方案,因为我只获得了大约一半的效率。非常感谢您的意见。

给定一个包含数字 0, 1, 2 和模式 [1, 2, 0, 2, 0, 2, 0] 的矩阵,从矩阵中的任意点开始找到匹配模式的最大长度,但只能沿对角线行驶。

这是我们期望函数返回 12 的示例。

这是我们期望函数返回 7 的地方。

空矩阵应该返回 0。

这是我的代码,就像我之前所说的那样,它确实有效并通过了所有测试,但我得到了停靠点。

def max_diagonal_match(matrix) -> int:
    max_len = 0
    pattern_match = [1, 2, 0, 2, 0, 2, 0]
    for i in range(0, len(matrix)):
        for j in range(0, len(matrix[i])):
            max_len = max(
                max_len,
                find_i_minus_j_minus(matrix, i, j, pattern_match, 7),
                find_i_minus_j_plus(matrix, i, j, pattern_match, 7),
                find_i_plus_j_minus(matrix, i, j, pattern_match, 7),
                find_i_plus_j_plus(matrix, i, j, pattern_match, 7)
            )
    return max_len


# i-, j-
def find_i_minus_j_minus(matrix, i, j, pattern, mod):
    count = 0
    while i >= 0 and j >= 0:
        if matrix[i][j] != pattern[count % mod]:
            return count
        count += 1
        i -= 1
        j -= 1
    return count


# i-, j+
def find_i_minus_j_plus(matrix, i, j, pattern, mod):
    count = 0
    while i >= 0 and j < len(matrix[i]):
        if matrix[i][j] != pattern[count % mod]:
            return count
        count += 1
        i -= 1
        j += 1
    return count


# i+, j-
def find_i_plus_j_minus(matrix, i, j, pattern, mod):
    count = 0
    while i < len(matrix) and j >= 0:
        if matrix[i][j] != pattern[count % mod]:
            return count
        count += 1
        i += 1
        j -= 1
    return count


# j+, i+
def find_i_plus_j_plus(matrix, i, j, pattern, mod):
    count = 0
    while i < len(matrix) and j < len(matrix[i]):
        if matrix[i][j] != pattern[count % mod]:
            return count
        count += 1
        i += 1
        j += 1
    return count

非常感谢您花时间阅读,非常感谢您的任何意见。

【问题讨论】:

  • 匹配是否必须从模式的开头开始,或者它可以是一部分?

标签: python python-3.x algorithm matrix string-matching


【解决方案1】:

我们可以将问题分解为沿着矩阵的每个对角线找到可能的最长匹配。然后算法的效率归结为我们能多快找到沿任何给定对角线的最长匹配项。

对于长度为N 的对角线和长度为M 的模式,您的解决方案是O(N^2):您尝试沿对角线的每个N 起点并尽可能延长匹配(是O(N)比较)

确实存在解决此问题的更快方法。可以用复杂度匹配每个对角线:

  • O(NM) 使用动态编程,如果 M &lt;&lt; N 会更快
  • O(NlogN) 按字典顺序对对角线的后缀进行排序(或相关技术:后缀树、Burrows-Wheeler 变换、FM 索引等)
  • 从技术上讲,O(N) 可以使用后缀数组,但算法过于复杂而无法实现编码面试(例如 Ukkonen 算法)

还有一些算法,虽然 O(N^2) 在最坏的情况下,在实践中可能比这快得多,例如当长匹配很少时,种子和扩展算法很快。

我确信上述技术并非详尽无遗。

【讨论】:

    【解决方案2】:

    this question 的接受答案为基础。它基于几个关键思想:

    您可以在一个范围内使用np.diagonal 将每个可能的对角线切出数组。您可以翻转阵列以确保获得所有角度。

    您可以tile 图案以确保它至少与最大对角线一样长或更长。

    您可以zip 对角线和图案,图案中的额外值将由于 zip 的工作方式而自动删除。

    然后您可以在压缩后的(diag,pattern)上使用takewhile 来检查len(set(x))==1 有多少连续匹配。

    如果您对所有可能的组合都这样做并取最大值,那么您应该会得到答案。

    import numpy as np
    from math import ceil
    from itertools import takewhile
    
    def max_sequence(arr):
        solns = []
        i = arr.shape[0]
        for x in range(-i, i+1):
            values = arr.diagonal(x)
            N = len(values)
            possibles = np.where(values == pattern[0])[0]
            for p in possibles:
                check = values[p:p+N]
                m = len(list(takewhile(lambda x:len(set(x))==1, zip(pattern,check))))
                solns.append(m)
        return max(solns)
    
    def find_longest(arr):
        if len(arr)>0:
            return max([max_sequence(x) for x in [arr, np.fliplr(arr), np.flipud(arr), arr[::-1]]])
        else:
            return 0
    
    arr = np.array([
        [1,0,2,1,1,1,1,0,0,0,0,0,0],
        [1,2,2,1,1,1,1,0,0,0,0,0,0],
        [1,0,0,1,1,1,1,0,0,0,0,0,0],
        [1,0,0,2,1,1,1,0,0,0,0,0,0],
        [1,0,0,2,0,1,1,0,0,0,0,0,0],
        [1,0,0,1,1,2,1,0,0,0,0,0,0],
        [1,0,0,1,1,1,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,1,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,2,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0],
        [0,0,0,0,0,0,0,0,0,0,2,0,0],
        [0,0,0,0,0,0,0,0,0,0,0,0,0],
    ])
    
    arr1 = np.array([
        [1,0,2,1,1,1,1],
        [1,2,2,1,1,1,1],
        [1,0,0,1,1,1,1],
        [1,0,0,2,1,1,1],
        [1,0,0,2,0,1,1],
        [1,0,0,1,1,2,1],
        [1,0,0,1,1,1,0]
    ])
    
    arr2 = np.array([])
    
    pattern = [1, 2, 0, 2, 0, 2, 0]
    # Make sure pattern repeats longer than the max diagonal
    pattern = np.tile(pattern,ceil(arr.shape[1] / len(pattern)))
    
    for a in [arr, arr1, arr2]:
        print(find_longest(a))
    

    输出

    12
    7
    0
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-31
      • 2017-10-30
      • 2011-02-24
      • 1970-01-01
      相关资源
      最近更新 更多