📜  给定矩阵中回文加路径的计数(1)

📅  最后修改于: 2023-12-03 14:56:54.886000             🧑  作者: Mango

给定矩阵中回文加路径的计数介绍

简介

本文将介绍一个问题:给定一个矩阵,计算其中的回文加路径的数量。具体而言,对于一个矩阵中的每个元素,以其作为起点,沿着上下左右四个方向能够构成的回文加路径的数量之和即为所求。

问题描述

设一个 $N \times N$ 的矩阵 $M=(m_{i,j})$,其中的元素 $m_{i,j}$ 取值为 $[1,26]$ 中的整数。以每个矩阵中的元素为起点,向上下左右四个方向延伸,形成一个长度不小于 2 的字符串。如果该字符串是一个回文串,计数器加 1。对所有起点计数器之和即为所求。

请以 Python 语言实现一个函数 count_palindrome_paths(matrix: List[List[int]]) -> int,输入为一个 $N \times N$ 的整数矩阵,输出为所求计数器的值。

示例

示例 1:

输入:
[
    [1, 2, 3],
    [2, 3, 4],
    [3, 4, 5]
]

输出:41

解释:
起点 (1,1) 可以构成的回文加路径:1, 2, 1, 3, 1, 4, 1, 5, 5, 1, 4, 1, 3, 1, 2, 1;
起点 (1,2) 可以构成的回文加路径:2, 1, 2, 3, 2, 4, 2, 5, 5, 2, 4, 2, 3, 2, 1, 2;
起点 (1,3) 可以构成的回文加路径:3, 2, 1, 2, 3, 4, 3, 5, 5, 3, 4, 3, 2, 1, 2, 3;
起点 (2,1) 可以构成的回文加路径:2, 1, 2, 3, 2, 4, 2, 5, 5, 2, 4, 2, 3, 2, 1, 2;
起点 (2,2) 可以构成的回文加路径:3, 2, 1, 2, 3, 4, 3, 5, 5, 3, 4, 3, 2, 1, 2, 3;
起点 (2,3) 可以构成的回文加路径:4, 3, 2, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 2, 3, 4;
起点 (3,1) 可以构成的回文加路径:3, 2, 1, 2, 3, 4, 3, 5, 5, 3, 4, 3, 2, 1, 2, 3;
起点 (3,2) 可以构成的回文加路径:4, 3, 2, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 2, 3, 4;
起点 (3,3) 可以构成的回文加路径:5, 4, 3, 2, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 2, 3。

计数器之和为:41。
解题思路

首先,我们注意到题目中的“回文加路径”是指以某个元素为起点,延伸四个方向构成的长度不小于 2 的字符串是回文串。我们可以把这个字符串分成中心和边界,中心是重叠部分,边界是四个方向中各取一个位置所对应的字符。

例如,以矩阵中一个元素 $(i,j)$ 为中心,那么沿着上下左右四个方向,可以构成一组回文字符串,如下所示:

$$ m_{i,j-1}\ m_{i,j}\ m_{i,j+1}\ \to\ m_{i-(k-1),j}\ m_{i-k,j}\ m_{i,j}\ m_{i+k,j}\ m_{i+(k-1),j} $$

其中,$k$ 为字符序列的中心到边界的距离。

假设以 $(i,j)$ 为中心的回文串数量为 $P(i,j)$,那么 $P(i,j)$ 等于以 $(i,j)$ 为中心左右扩展所能得到的回文串数量 $S(i,j)$,再×4,然后再加 $P(i-1,j)$、$P(i+1,j)$、$P(i,j-1)$、$P(i,j+1)$ 四个位置所组成的回文串数量。即:

$$ P(i,j) = 4S(i,j) + P(i-1,j) + P(i+1,j) + P(i,j-1) + P(i,j+1) $$

考虑如何计算 $S(i,j)$。容易发现,$S(i,j)$ 的值与从 $(i,j)$ 出发向上、下、左、右四个方向分别扩展得到的回文串的长度有关。具体而言,我们设 $L_1(i,j)$、$L_2(i,j)$、$L_3(i,j)$、$L_4(i,j)$ 分别为从 $(i,j)$ 出发在上、下、左、右方向扩展得到的回文串的长度,则:

$$ \begin{aligned} S(i,j) &= \min{L_1(i,j), L_2(i,j), L_3(i,j), L_4(i,j)} \ &= \frac{\min{L_1(i,j)+L_2(i,j)-1, L_3(i,j)+L_4(i,j)-1}}{2} \end{aligned} $$

对于 $L_1(i,j)$、$L_2(i,j)$、$L_3(i,j)$、$L_4(i,j)$ 的求解,可以分别沿着上下左右四个方向同时扫描,直到遇到不同的数为止。算法时间复杂度为 $O(N^2)$,空间复杂度为 $O(N^2)$。

解题代码
from typing import List

def count_palindrome_paths(matrix: List[List[int]]) -> int:
    n = len(matrix)
    S = [[0] * n for _ in range(n)] # 存储每个 (i,j) 位置上的 S 值
    P = [[0] * n for _ in range(n)] # 存储每个 (i,j) 位置上的 P 值

    def get_L(i, j, di, dj):
        x, y = i + di, j + dj # 从 (i,j) 出发到下一个位置 (x,y)
        L = 1
        while x >= 0 and x < n and y >= 0 and y < n and matrix[x][y] == matrix[i][j]:
            x += di
            y += dj
            L += 1
        return L

    # 计算 S 值和 P 值
    for i in range(n):
        for j in range(n):
            L1, L2, L3, L4 = get_L(i, j, -1, 0), get_L(i, j, 1, 0), get_L(i, j, 0, -1), get_L(i, j, 0, 1)
            S[i][j] = min(L1, L2, L3, L4)
            P[i][j] = 4 * S[i][j] + P[i-1][j] + P[i+1][j] + P[i][j-1] + P[i][j+1] if S[i][j] > 1 else 0

    return sum(sum(row) for row in P)