562. 矩阵中最长的连续1线段
给定一个01矩阵 mat,找到矩阵中最长的连续1线段。这条线段可以是水平的、垂直的、对角线的或者反对角线的。
示例:
输入:
[[0,1,1,0],
[0,1,1,0],
[0,0,0,1]]
输出: 3
提示: 给定矩阵中的元素数量不会超过 10,000。
解法一:标准 DP
思想
状态表示
- f ( 0 , i , j ) f(0,i,j) f(0,i,j) 表示以 ( i , j ) (i,j) (i,j) 点为结尾的 水平 连续 1 线段的长度
- f ( 1 , i , j ) f(1,i,j) f(1,i,j) 表示以 ( i , j ) (i,j) (i,j) 点为结尾的 垂直 连续 1 线段的长度
- f ( 2 , i , j ) f(2,i,j) f(2,i,j) 表示以 ( i , j ) (i,j) (i,j) 点为结尾的 对角 连续 1 线段的长度
- f ( 3 , i , j ) f(3,i,j) f(3,i,j) 表示以 ( i , j ) (i,j) (i,j) 点为结尾的 反对角 连续 1 线段的长度
此处 i , j i,j i,j 从 1 1 1 开始,题中所给矩阵的下标从 0 0 0 开始,所以 f f f 中的 i , j i,j i,j 对应与 m a t mat mat 的 i − 1 , j − 1 i-1,j-1 i−1,j−1
状态转移
-
以 ( i , j ) (i,j) (i,j) 点结尾的 水平 连续 1 线段的长度与以 ( i , j − 1 ) (i, j - 1) (i,j−1) 点结尾的 水平 连续 1 线段的长度有关
即 f ( 0 , i , j , 0 ) f(0,i,j,0) f(0,i,j,0) 由 f ( 0 , i , j − 1 ) f(0,i,j - 1) f(0,i,j−1) 转移
-
以 ( i , j ) (i,j) (i,j) 点结尾的 垂直 连续 1 线段的长度与以 ( i − 1 , j ) (i - 1, j) (i−1,j) 点结尾的 垂直 连续 1 线段的长度有关
即 f ( 1 , i , j ) f(1,i,j) f(1,i,j) 由 f ( 1 , i − 1 , j ) f(1,i - 1 ,j) f(1,i−1,j) 转移
-
以 ( i , j ) (i,j) (i,j) 点结尾的 对角 连续 1 线段的长度与以 ( i − 1 , j − 1 ) (i - 1, j-1) (i−1,j−1) 点结尾的 对角 连续 1 线段的长度有关
即 f ( 2 , i , j ) f(2,i,j) f(2,i,j) 由 f ( 2 , i − 1 , j − 1 ) f(2,i - 1,j-1) f(2,i−1,j−1) 转移
-
以 ( i , j ) (i,j) (i,j) 点结尾的 反对角 连续 1 线段的长度与以 ( i − 1 , j + 1 ) (i - 1, j + 1) (i−1,j+1) 点结尾的 反对角 连续 1 线段的长度有关
即 f ( 3 , i , j ) f(3,i,j) f(3,i,j) 由 f ( 3 , i − 1 , j + 1 ) f(3,i - 1,j + 1) f(3,i−1,j+1) 转移
状态计算
- 当 ( i , j ) (i,j) (i,j) 点为 1 时,连续 1 的长度增长 1
- 当 ( i , j ) (i,j) (i,j) 点为 0 0 0 时,连续 1 中断,所以连续 1 为 0
复杂度
时间复杂度: O ( N M ) O(NM) O(NM), N N N 为 m a t mat mat 行数, M M M 为 m a t mat mat 列数
空间复杂度: O ( N M ) O(NM) O(NM)
代码
class Solution {
public int longestLine(int[][] mat) {
int n = mat.length, m = mat[0].length;
int[][][] f = new int[4][n + 2][m + 2];
int res = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
boolean flag = mat[i - 1][j - 1] == 1;
f[0][i][j] = flag ? f[0][i + 0][j - 1] + 1 : 0;
f[1][i][j] = flag ? f[1][i - 1][j + 0] + 1 : 0;
f[2][i][j] = flag ? f[2][i - 1][j - 1] + 1 : 0;
f[3][i][j] = flag ? f[3][i - 1][j + 1] + 1 : 0;
for (int k = 0; k < 4; k++) {
res = Math.max(res, f[k][i][j]);
}
}
}
return res;
}
}
解法二:滚动数组优化
思想
由解法一可得所有状态递推公式
f[0][i][j] = f[0][i + 0][j - 1] + 1;
f[1][i][j] = f[1][i - 1][j + 0] + 1;
f[2][i][j] = f[2][i - 1][j - 1] + 1;
f[3][i][j] = f[3][i - 1][j + 1] + 1;
除
k
=
0
k = 0
k=0 外,对于 f[i]
来说只依赖于 f[i - 1]
,所以可以用两组数组滚动更新。
复杂度
时间复杂度: O ( N M ) O(NM) O(NM)
空间复杂度: O ( M ) O(M) O(M)
代码
class Solution {
public int longestLine(int[][] mat) {
int n = mat.length, m = mat[0].length;
int[][] f = new int[4][m + 2];
int res = 0;
for (int i = 1; i <= n; i++) {
int[][] g = new int[4][m + 2];
for (int j = 1; j <= m; j++) {
boolean flag = mat[i - 1][j - 1] == 1;
g[0][j] = flag ? g[0][j - 1] + 1 : 0;
g[1][j] = flag ? f[1][j + 0] + 1 : 0;
g[2][j] = flag ? f[2][j - 1] + 1 : 0;
g[3][j] = flag ? f[3][j + 1] + 1 : 0;
for (int k = 0; k < 4; k++) {
res = Math.max(res, g[k][j]);
}
}
for (int k = 0; k < 4; k++) {
f[k] = g[k];
}
}
return res;
}
}