package com.freetymekiyan.algorithms.level.medium;

/**
 * Given a 2D binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area.
 * <p>
 * For example, given the following matrix:
 * <p>
 * 1 0 1 0 0
 * 1 0 1 1 1
 * 1 1 1 1 1
 * 1 0 0 1 0
 * Return 4.
 * <p>
 * Company Tags: Apple, Airbnb, Facebook
 * Tags: Dynamic Programming
 * Similar Problems: (H) Maximal Rectangle
 */
public class MaximalSquare {

    /**
     * DP.
     * Finding the largest square's area is the same as finding the edge length.
     * The recurrence relation here is:
     * Suppose the largest edge length formed by current grid at matrix[i-1][j-1] is dp[i][j].
     * If matrix[i - 1][j - 1] = 1, dp[i][j] = min(dp[i-1][j], dp[i][j], dp[i][j-1]) + 1.
     * If matrix[i - 1][j - 1] = 0, dp[i][j] = 0.
     * <p>
     * To understand the recurrence relation, draw a matrix.
     * Iff grid [i,j] is 1 and it's just a corners of other 1s, can the length expand.
     */
    public int maximalSquare(char[][] matrix) {
        if (matrix == null) {
            return 0;
        }
        int r = matrix.length;
        int c = r == 0 ? 0 : matrix[0].length;
        int[][] dp = new int[r + 1][c + 1]; // First row and column are all 0.
        int maxLen = 0;
        for (int i = 1; i <= r; i++) { // Traverse dp array. Note the equal sign.
            for (int j = 1; j <= c; j++) {
                if (matrix[i - 1][j - 1] == '1') {
                    // Update dp[i][j] and the max length.
                    dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
                    maxLen = Math.max(maxLen, dp[i][j]);
                }
            }
        }
        return maxLen * maxLen; // Return AREA here.
    }

    /**
     * DP. Space optimized.
     * Only the previous row and previous column is needed.
     * So reduce space usage to an array and an integer.
     */
    public int maximalSquareB(char[][] matrix) {
        if (matrix == null) {
            return 0;
        }
        int r = matrix.length;
        int c = r == 0 ? 0 : matrix[0].length;
        int[] dp = new int[c + 1]; // Only need one row.
        int prev = 0; // Store dp[i-1][j-1].
        int maxLen = 0;
        for (int i = 1; i <= r; i++) {
            for (int j = 1; j <= c; j++) {
                int temp = dp[j]; // Store dp[i-1][j-1] of next iteration.
                if (matrix[i - 1][j - 1] == '1') {
                    dp[j] = Math.min(Math.min(dp[j - 1], dp[j]), prev) + 1;
                    maxLen = Math.max(maxLen, dp[j]);
                } else {
                    dp[j] = 0; // Have to update when grid is 0.
                }
                prev = temp; // dp[j] before update is the dp[i-1][j-1] for the next loop.
            }
        }
        return maxLen * maxLen; // Return the AREA here.
    }

}

@@ -1857,18 +1857,19 @@
  column 
-is
+are
  needed.

update maximal square