You are given a map of a server center, represented as a `m * n` integer
matrix `grid`, where 1 means that on that cell there is a server and 0 means
that it is no server. Two servers are said to communicate if they are on the
same row or on the same column.

Return the number of servers that communicate with any other server.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def countServers(self, grid: List[List[int]]) -> int:
    m = len(grid)
    n = len(grid[0])
    ans = 0
    rows = [0] * m
    cols = [0] * n

    for i in range(m):
      for j in range(n):
        if grid[i][j] == 1:
          rows[i] += 1
          cols[j] += 1

    for i in range(m):
      for j in range(n):
        if grid[i][j] == 1 and (rows[i] > 1 or cols[j] > 1):
          ans += 1

    return ans

class Solution {
  public int countServers(int[][] grid) {
    final int m = grid.length;
    final int n = grid[0].length;
    int ans = 0;
    int[] rows = new int[m];
    int[] cols = new int[n];

    for (int i = 0; i < m; ++i)
      for (int j = 0; j < n; ++j)
        if (grid[i][j] == 1) {
          ++rows[i];
          ++cols[j];
        }

    for (int i = 0; i < m; ++i)
      for (int j = 0; j < n; ++j)
        if (grid[i][j] == 1 && (rows[i] > 1 || cols[j] > 1))
          ++ans;

    return ans;
  }
}

class Solution {
 public:
  int countServers(vector<vector<int>>& grid) {
    const int m = grid.size();
    const int n = grid[0].size();
    int ans = 0;
    vector<int> rows(m);
    vector<int> cols(n);

    for (int i = 0; i < m; ++i)
      for (int j = 0; j < n; ++j)
        if (grid[i][j] == 1) {
          ++rows[i];
          ++cols[j];
        }

    for (int i = 0; i < m; ++i)
      for (int j = 0; j < n; ++j)
        if (grid[i][j] == 1 && (rows[i] > 1 || cols[j] > 1))
          ++ans;

    return ans;
  }
};


Minimum Number of Flips to Convert Binary Matrix to Zero Matrix

Given a `m x n` binary matrix `mat`. In one step, you can choose one cell and
flip it and all the four neighbors of it if they exist (Flip is changing `1`
to `0` and `0` to `1`). A pair of cells are called neighbors if they share one
edge.

Return the minimum number of steps required to convert `mat` to a zero matrix
or `-1` if you cannot.

A binary matrix is a matrix with all cells equal to `0` or `1` only.

A zero matrix is a matrix with all cells equal to `0`.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def minFlips(self, mat: List[List[int]]) -> int:
    m = len(mat)
    n = len(mat[0])
    hash = self._getHash(mat, m, n)
    if hash == 0:
      return 0

    dirs = ((0, 1), (1, 0), (0, -1), (-1, 0))
    step = 0
    q = collections.deque([hash])
    seen = {hash}

    while q:
      step += 1
      for _ in range(len(q)):
        curr = q.popleft()
        for i in range(m):
          for j in range(n):
            next = curr ^ 1 << (i * n + j)
            for dx, dy in dirs:
              x = i + dx
              y = j + dy
              if x < 0 or x == m or y < 0 or y == n:
                continue
              next ^= 1 << (x * n + y)
            if next == 0:
              return step
            if next in seen:
              continue
            q.append(next)
            seen.add(next)

    return -1

  def _getHash(self, mat: List[List[int]], m: int, n: int) -> int:
    hash = 0
    for i in range(m):
      for j in range(n):
        if mat[i][j]:
          hash |= 1 << (i * n + j)
    return hash

class Solution {
  public int minFlips(int[][] mat) {
    final int m = mat.length;
    final int n = mat[0].length;
    final int hash = getHash(mat, m, n);
    if (hash == 0)
      return 0;

    final int[][] DIRS = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    Queue<Integer> q = new ArrayDeque<>(List.of(hash));
    Set<Integer> seen = new HashSet<>(Arrays.asList(hash));

    for (int step = 1; !q.isEmpty(); ++step) {
      for (int sz = q.size(); sz > 0; --sz) {
        final int curr = q.poll();
        for (int i = 0; i < m; ++i) {
          for (int j = 0; j < n; ++j) {
            int next = curr ^ 1 << (i * n + j);
            // Flie the four neighbors.
            for (int[] dir : DIRS) {
              final int x = i + dir[0];
              final int y = j + dir[1];
              if (x < 0 || x == m || y < 0 || y == n)
                continue;
              next ^= 1 << (x * n + y);
            }
            if (next == 0)
              return step;
            if (seen.contains(next))
              continue;
            q.offer(next);
            seen.add(next);
          }
        }
      }
    }

    return -1;
  }

  private int getHash(int[][] mat, int m, int n) {
    int hash = 0;
    for (int i = 0; i < m; ++i)
      for (int j = 0; j < n; ++j)
        if (mat[i][j] == 1)
          hash |= 1 << (i * n + j);
    return hash;
  }
}

class Solution {
 public:
  int minFlips(vector<vector<int>>& mat) {
    const int m = mat.size();
    const int n = mat[0].size();
    const int hash = getHash(mat, m, n);
    if (hash == 0)
      return 0;

    constexpr int kDirs[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    queue<int> q{{hash}};
    unordered_set<int> seen{hash};

    for (int step = 1; !q.empty(); ++step) {
      for (int sz = q.size(); sz > 0; --sz) {
        const int curr = q.front();
        q.pop();
        for (int i = 0; i < m; ++i) {
          for (int j = 0; j < n; ++j) {
            int next = curr ^ 1 << (i * n + j);
            // Flie the four neighbors.
            for (const auto& [dx, dy] : kDirs) {
              const int x = i + dx;
              const int y = j + dy;
              if (x < 0 || x == m || y < 0 || y == n)
                continue;
              next ^= 1 << (x * n + y);
            }
            if (next == 0)
              return step;
            if (seen.contains(next))
              continue;
            q.push(next);
            seen.insert(next);
          }
        }
      }
    }

    return -1;
  }

 private:
  int getHash(const vector<vector<int>>& mat, int m, int n) {
    int hash = 0;
    for (int i = 0; i < m; ++i)
      for (int j = 0; j < n; ++j)
        if (mat[i][j])
          hash |= 1 << (i * n + j);
    return hash;
  }
};


Shortest Path in a Grid with Obstacles Elimination

You are given an `m x n` integer matrix `grid` where each cell is either `0`
(empty) or `1` (obstacle). You can move up, down, left, or right from and to
an empty cell in one step.

Return the minimum number of steps to walk from the upper left corner `(0, 0)`
to the lower right corner `(m - 1, n - 1)` given that you can eliminate at
most `k` obstacles. If it is not possible to find such walk return `-1`.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def shortestPath(self, grid: List[List[int]], k: int) -> int:
    m = len(grid)
    n = len(grid[0])
    if m == 1 and n == 1:
      return 0

    dirs = ((0, 1), (1, 0), (0, -1), (-1, 0))
    steps = 0
    q = collections.deque([(0, 0, k)])
    seen = {(0, 0, k)}

    while q:
      steps += 1
      for _ in range(len(q)):
        i, j, eliminate = q.popleft()
        for l in range(4):
          x = i + dirs[l][0]
          y = j + dirs[l][1]
          if x < 0 or x == m or y < 0 or y == n:
            continue
          if x == m - 1 and y == n - 1:
            return steps
          if grid[x][y] == 1 and eliminate == 0:
            continue
          newEliminate = eliminate - grid[x][y]
          if (x, y, newEliminate) in seen:
            continue
          q.append((x, y, newEliminate))
          seen.add((x, y, newEliminate))

    return -1

class Solution {
  public int shortestPath(int[][] grid, int k) {
    record T(int i, int j, int eliminate) {}
    final int m = grid.length;
    final int n = grid[0].length;
    if (m == 1 && n == 1)
      return 0;

    final int[][] DIRS = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    Queue<T> q = new ArrayDeque<>(List.of(new T(0, 0, k)));
    boolean[][][] seen = new boolean[m][n][k + 1];
    seen[0][0][k] = true;

    for (int step = 1; !q.isEmpty(); ++step)
      for (int sz = q.size(); sz > 0; --sz) {
        final int i = q.peek().i;
        final int j = q.peek().j;
        final int eliminate = q.poll().eliminate;
        for (int l = 0; l < 4; ++l) {
          final int x = i + DIRS[l][0];
          final int y = j + DIRS[l][1];
          if (x < 0 || x == m || y < 0 || y == n)
            continue;
          if (x == m - 1 && y == n - 1)
            return step;
          if (grid[x][y] == 1 && eliminate == 0)
            continue;
          final int newEliminate = eliminate - grid[x][y];
          if (seen[x][y][newEliminate])
            continue;
          q.offer(new T(x, y, newEliminate));
          seen[x][y][newEliminate] = true;
        }
      }

    return -1;
  }
}

class Solution {
 public:
  int shortestPath(vector<vector<int>>& grid, int k) {
    const int m = grid.size();
    const int n = grid[0].size();
    if (m == 1 && n == 1)
      return 0;

    constexpr int kDirs[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    queue<tuple<int, int, int>> q{{{0, 0, k}}};  // (i, j, eliminate)
    vector<vector<vector<bool>>> seen(
        m, vector<vector<bool>>(n, vector<bool>(k + 1)));
    seen[0][0][k] = true;

    for (int step = 1; !q.empty(); ++step)
      for (int sz = q.size(); sz > 0; --sz) {
        const auto [i, j, eliminate] = q.front();
        q.pop();
        for (const auto& [dx, dy] : kDirs) {
          const int x = i + dx;
          const int y = j + dy;
          if (x < 0 || x == m || y < 0 || y == n)
            continue;
          if (x == m - 1 && y == n - 1)
            return step;
          if (grid[x][y] == 1 && eliminate == 0)
            continue;
          const int newEliminate = eliminate - grid[x][y];
          if (seen[x][y][newEliminate])
            continue;
          q.emplace(x, y, newEliminate);
          seen[x][y][newEliminate] = true;
        }
      }

    return -1;
  }
};


You are given a square `board` of characters. You can move on the board
starting at the bottom right square marked with the character `'S'`.

You need to reach the top left square marked with the character `'E'`. The
rest of the squares are labeled either with a numeric character `1, 2, ..., 9`
or with an obstacle `'X'`. In one move you can go up, left or up-left
(diagonally) only if there is no obstacle there.

Return a list of two integers: the first integer is the maximum sum of numeric
characters you can collect, and the second is the number of such paths that
you can take to get that maximum sum, taken modulo `10^9 + 7`.

In case there is no path, return `[0, 0]`.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def pathsWithMaxScore(self, board: List[str]) -> List[int]:
    kMod = 1_000_000_007
    n = len(board)
    dirs = ((0, 1), (1, 0), (1, 1))
    dp = [[-1] * (n + 1) for _ in range(n + 1)]
    count = [[0] * (n + 1) for _ in range(n + 1)]

    dp[0][0] = 0
    dp[n - 1][n - 1] = 0
    count[n - 1][n - 1] = 1

    for i in reversed(range(n)):
      for j in reversed(range(n)):
        if board[i][j] == 'S' or board[i][j] == 'X':
          continue
        for dx, dy in dirs:
          x = i + dx
          y = j + dy
          if dp[i][j] < dp[x][y]:
            dp[i][j] = dp[x][y]
            count[i][j] = count[x][y]
          elif dp[i][j] == dp[x][y]:
            count[i][j] += count[x][y]
            count[i][j] %= kMod

        if dp[i][j] != -1 and board[i][j] != 'E':
          dp[i][j] += int(board[i][j])
          dp[i][j] %= kMod

    return [dp[0][0], count[0][0]]

class Solution {
  public int[] pathsWithMaxScore(List<String> board) {
    final int MOD = 1_000_000_007;
    final int[][] DIRS = {{0, 1}, {1, 0}, {1, 1}};
    final int n = board.size();
    // dp[i][j] := the maximum sum from (n - 1, n - 1) to (i, j)
    int[][] dp = new int[n + 1][n + 1];
    Arrays.stream(dp).forEach(A -> Arrays.fill(A, -1));
    // count[i][j] := the number of paths to get dp[i][j] from (n - 1, n - 1) to (i, j)
    int[][] count = new int[n + 1][n + 1];

    dp[0][0] = 0;
    dp[n - 1][n - 1] = 0;
    count[n - 1][n - 1] = 1;

    for (int i = n - 1; i >= 0; --i)
      for (int j = n - 1; j >= 0; --j) {
        if (board.get(i).charAt(j) == 'S' || board.get(i).charAt(j) == 'X')
          continue;
        for (int[] dir : DIRS) {
          final int x = i + dir[0];
          final int y = j + dir[1];
          if (dp[i][j] < dp[x][y]) {
            dp[i][j] = dp[x][y];
            count[i][j] = count[x][y];
          } else if (dp[i][j] == dp[x][y]) {
            count[i][j] += count[x][y];
            count[i][j] %= MOD;
          }
        }
        // If there's path(s) from 'S' to (i, j) and the cell is not 'E'.
        if (dp[i][j] != -1 && board.get(i).charAt(j) != 'E') {
          dp[i][j] += board.get(i).charAt(j) - '0';
          dp[i][j] %= MOD;
        }
      }

    return new int[] {dp[0][0], count[0][0]};
  }
}

class Solution {
 public:
  vector<int> pathsWithMaxScore(vector<string>& board) {
    constexpr int kMod = 1'000'000'007;
    constexpr int kDirs[3][2] = {{0, 1}, {1, 0}, {1, 1}};
    const int n = board.size();
    // dp[i][j] := the maximum sum from (n - 1, n - 1) to (i, j)
    vector<vector<int>> dp(n + 1, vector<int>(n + 1, -1));
    // count[i][j] := the number of paths to get dp[i][j] from (n - 1, n - 1) to
    // (i, j)
    vector<vector<int>> count(n + 1, vector<int>(n + 1));

    dp[0][0] = 0;
    dp[n - 1][n - 1] = 0;
    count[n - 1][n - 1] = 1;

    for (int i = n - 1; i >= 0; --i)
      for (int j = n - 1; j >= 0; --j) {
        if (board[i][j] == 'S' || board[i][j] == 'X')
          continue;
        for (const auto& [dx, dy] : kDirs) {
          const int x = i + dx;
          const int y = j + dy;
          if (dp[i][j] < dp[x][y]) {
            dp[i][j] = dp[x][y];
            count[i][j] = count[x][y];
          } else if (dp[i][j] == dp[x][y]) {
            count[i][j] += count[x][y];
            count[i][j] %= kMod;
          }
        }
        // If there's path(s) from 'S' to (i, j) and the cell is not 'E'.
        if (dp[i][j] != -1 && board[i][j] != 'E') {
          dp[i][j] += board[i][j] - '0';
          dp[i][j] %= kMod;
        }
      }

    return {dp[0][0], count[0][0]};
  }
};


Find the City With the Smallest Number of Neighbors at a Threshold Distance

There are `n` cities numbered from `0` to `n-1`. Given the array `edges` where
`edges[i] = [fromi, toi, weighti]` represents a bidirectional and weighted
edge between cities `fromi` and `toi`, and given the integer
`distanceThreshold`.

Return the city with the smallest number of cities that are reachable through
some path and whose distance is at most `distanceThreshold`, If there are
multiple such cities, return the city with the greatest number.

Notice that the distance of a path connecting cities i and j is equal to the
sum of the edges' weights along that path.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def findTheCity(self, n: int, edges: List[List[int]], distanceThreshold: int) -> int:
    ans = -1
    minCitiesCount = n
    dist = self._floydWarshall(n, edges, distanceThreshold)

    for i in range(n):
      citiesCount = sum(dist[i][j] <= distanceThreshold for j in range(n))
      if citiesCount <= minCitiesCount:
        ans = i
        minCitiesCount = citiesCount

    return ans

  def _floydWarshall(self, n: int, edges: List[List[int]], distanceThreshold: int) -> List[List[int]]:
    dist = [[distanceThreshold + 1] * n for _ in range(n)]

    for i in range(n):
      dist[i][i] = 0

    for u, v, w in edges:
      dist[u][v] = w
      dist[v][u] = w

    for k in range(n):
      for i in range(n):
        for j in range(n):
          dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])

    return dist

class Solution {
  public int findTheCity(int n, int[][] edges, int distanceThreshold) {
    int ans = -1;
    int minCitiesCount = n;
    int[][] dist = floydWarshall(n, edges, distanceThreshold);

    for (int i = 0; i < n; ++i) {
      int citiesCount = 0;
      for (int j = 0; j < n; ++j)
        if (dist[i][j] <= distanceThreshold)
          ++citiesCount;
      if (citiesCount <= minCitiesCount) {
        ans = i;
        minCitiesCount = citiesCount;
      }
    }

    return ans;
  }

  private int[][] floydWarshall(int n, int[][] edges, int distanceThreshold) {
    int[][] dist = new int[n][n];
    Arrays.stream(dist).forEach(A -> Arrays.fill(A, distanceThreshold + 1));

    for (int i = 0; i < n; ++i)
      dist[i][i] = 0;

    for (int[] edge : edges) {
      final int u = edge[0];
      final int v = edge[1];
      final int w = edge[2];
      dist[u][v] = w;
      dist[v][u] = w;
    }

    for (int k = 0; k < n; ++k)
      for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
          dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);

    return dist;
  }
}

class Solution {
 public:
  int findTheCity(int n, vector<vector<int>>& edges, int distanceThreshold) {
    int ans = -1;
    int minCitiesCount = n;
    const vector<vector<int>> dist = floydWarshall(n, edges, distanceThreshold);

    for (int i = 0; i < n; ++i) {
      int citiesCount = 0;
      for (int j = 0; j < n; ++j)
        if (dist[i][j] <= distanceThreshold)
          ++citiesCount;
      if (citiesCount <= minCitiesCount) {
        ans = i;
        minCitiesCount = citiesCount;
      }
    }

    return ans;
  }

 private:
  vector<vector<int>> floydWarshall(int n, const vector<vector<int>>& edges,
                                    int distanceThreshold) {
    vector<vector<int>> dist(n, vector<int>(n, distanceThreshold + 1));

    for (int i = 0; i < n; ++i)
      dist[i][i] = 0;

    for (const vector<int>& edge : edges) {
      const int u = edge[0];
      const int v = edge[1];
      const int w = edge[2];
      dist[u][v] = w;
      dist[v][u] = w;
    }

    for (int k = 0; k < n; ++k)
      for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
          dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);

    return dist;
  }
};


Given an array of integers `arr` and an integer `d`. In one step you can jump
from index `i` to index:

* `i + x` where: `i + x < arr.length` and ` 0 < x <= d`.
* `i - x` where: `i - x >= 0` and ` 0 < x <= d`.

In addition, you can only jump from index `i` to index `j` if `arr[i] >
arr[j]` and `arr[i] > arr[k]` for all indices `k` between `i` and `j` (More
formally `min(i, j) < k < max(i, j)`).

You can choose any index of the array and start jumping. Return the maximum
number of indices you can visit.

Notice that you can not jump outside of the array at any time.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def maxJumps(self, arr: List[int], d: int) -> int:
    n = len(arr)
    dp = [1] * n
    stack = []

    for i in range(n + 1):
      while stack and (i == n or arr[stack[-1]] < arr[i]):
        indices = [stack.pop()]
        while stack and arr[stack[-1]] == arr[indices[0]]:
          indices.append(stack.pop())
        for j in indices:
          if i < n and i - j <= d:
            dp[i] = max(dp[i], dp[j] + 1)
          if stack and j - stack[-1] <= d:
            dp[stack[-1]] = max(dp[stack[-1]], dp[j] + 1)
      stack.append(i)

    return max(dp)

class Solution {
  public int maxJumps(int[] arr, int d) {
    final int n = arr.length;
    // dp[i] := the maximum jumps starting from arr[i]
    int[] dp = new int[n];
    // a dcreasing stack that stores indices
    Deque<Integer> stack = new ArrayDeque<>();

    for (int i = 0; i <= n; ++i) {
      while (!stack.isEmpty() && (i == n || arr[stack.peek()] < arr[i])) {
        List<Integer> indices = new ArrayList<>(List.of(stack.pop()));
        while (!stack.isEmpty() && arr[stack.peek()] == arr[indices.get(0)])
          indices.add(stack.pop());
        for (final int j : indices) {
          if (i < n && i - j <= d)
            // Can jump from i to j.
            dp[i] = Math.max(dp[i], dp[j] + 1);
          if (!stack.isEmpty() && j - stack.peek() <= d)
            // Can jump from stack.peek() to j
            dp[stack.peek()] = Math.max(dp[stack.peek()], dp[j] + 1);
        }
      }
      stack.push(i);
    }

    return Arrays.stream(dp).max().getAsInt() + 1;
  }
}

class Solution {
 public:
  int maxJumps(vector<int>& arr, int d) {
    const int n = arr.size();
    // dp[i] := the maximum jumps starting from arr[i]
    vector<int> dp(n, 1);
    // a dcreasing stack that stores indices
    stack<int> stack;

    for (int i = 0; i <= n; ++i) {
      while (!stack.empty() && (i == n || arr[stack.top()] < arr[i])) {
        vector<int> indices{stack.top()};
        stack.pop();
        while (!stack.empty() && arr[stack.top()] == arr[indices[0]])
          indices.push_back(stack.top()), stack.pop();
        for (const int j : indices) {
          if (i < n && i - j <= d)
            // Can jump from i to j.
            dp[i] = max(dp[i], dp[j] + 1);
          if (!stack.empty() && j - stack.top() <= d)
            // Can jump from stack[-1] to j.
            dp[stack.top()] = max(dp[stack.top()], dp[j] + 1);
        }
      }
      stack.push(i);
    }

    return ranges::max(dp);
  }
};


Given an array of integers `arr`, you are initially positioned at the first
index of the array.

In one step you can jump from index `i` to index:

* `i + 1` where: `i + 1 < arr.length`.
* `i - 1` where: `i - 1 >= 0`.
* `j` where: `arr[i] == arr[j]` and `i != j`.

Return the minimum number of steps to reach the last index of the array.

Notice that you can not jump outside of the array at any time.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def minJumps(self, arr: List[int]) -> int:
    n = len(arr)
    graph = collections.defaultdict(list)
    step = 0
    q = collections.deque([0])
    seen = {0}

    for i, a in enumerate(arr):
      graph[a].append(i)

    while q:
      for _ in range(len(q)):
        i = q.popleft()
        if i == n - 1:
          return step
        seen.add(i)
        u = arr[i]
        if i + 1 < n:
          graph[u].append(i + 1)
        if i - 1 >= 0:
          graph[u].append(i - 1)
        for v in graph[u]:
          if v in seen:
            continue
          q.append(v)
        graph[u].clear()
      step += 1

class Solution {
  public int minJumps(int[] arr) {
    final int n = arr.length;
    // {a: indices}
    Map<Integer, List<Integer>> graph = new HashMap<>();
    Queue<Integer> q = new ArrayDeque<>(List.of(0));
    boolean[] seen = new boolean[n];
    seen[0] = true;

    for (int i = 0; i < n; ++i) {
      graph.putIfAbsent(arr[i], new ArrayList<>());
      graph.get(arr[i]).add(i);
    }

    for (int step = 0; !q.isEmpty(); ++step) {
      for (int sz = q.size(); sz > 0; --sz) {
        final int i = q.poll();
        if (i == n - 1)
          return step;
        seen[i] = true;
        final int u = arr[i];
        if (i + 1 < n)
          graph.get(u).add(i + 1);
        if (i - 1 >= 0)
          graph.get(u).add(i - 1);
        for (final int v : graph.get(u)) {
          if (seen[v])
            continue;
          q.offer(v);
        }
        graph.get(u).clear();
      }
    }

    throw new IllegalArgumentException();
  }
}

class Solution {
 public:
  int minJumps(vector<int>& arr) {
    const int n = arr.size();
    // {a: indices}
    unordered_map<int, vector<int>> graph;
    queue<int> q{{0}};
    vector<bool> seen(n);
    seen[0] = true;

    for (int i = 0; i < n; ++i)
      graph[arr[i]].push_back(i);

    for (int step = 0; !q.empty(); ++step) {
      for (int sz = q.size(); sz > 0; --sz) {
        const int i = q.front();
        q.pop();
        if (i == n - 1)
          return step;
        seen[i] = true;
        const int u = arr[i];
        if (i + 1 < n)
          graph[u].push_back(i + 1);
        if (i - 1 >= 0)
          graph[u].push_back(i - 1);
        for (const int v : graph[u]) {
          if (seen[v])
            continue;
          q.push(v);
        }
        graph[u].clear();
      }
    }

    throw;
  }
};


Given an undirected tree consisting of `n` vertices numbered from `1` to `n`.
A frog starts jumping from vertex 1. In one second, the frog jumps from its
current vertex to another unvisited vertex if they are directly connected. The
frog can not jump back to a visited vertex. In case the frog can jump to
several vertices, it jumps randomly to one of them with the same probability.
Otherwise, when the frog can not jump to any unvisited vertex, it jumps
forever on the same vertex.

The edges of the undirected tree are given in the array `edges`, where
`edges[i] = [ai, bi]` means that exists an edge connecting the vertices `ai`
and `bi`.

Return the probability that after `t` seconds the frog is on the vertex
`target`. Answers within `10-5` of the actual answer will be accepted.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def frogPosition(self, n: int, edges: List[List[int]], t: int, target: int) -> float:
    tree = [[] for _ in range(n + 1)]
    q = collections.deque([1])
    seen = [False] * (n + 1)
    prob = [0] * (n + 1)

    prob[1] = 1
    seen[1] = True

    for u, v in edges:
      tree[u].append(v)
      tree[v].append(u)

    for _ in range(t):
      for _ in range(len(q)):
        a = q.popleft()
        nChildren = sum(not seen[b] for b in tree[a])
        for b in tree[a]:
          if seen[b]:
            continue
          seen[b] = True
          prob[b] = prob[a] / nChildren
          q.append(b)
        if nChildren > 0:
          prob[a] = 0

    return prob[target]

class Solution {
  public double frogPosition(int n, int[][] edges, int t, int target) {
    List<Integer>[] tree = new List[n + 1];
    Queue<Integer> q = new ArrayDeque<>(List.of(1));
    boolean[] seen = new boolean[n + 1];
    double[] prob = new double[n + 1];

    seen[1] = true;
    prob[1] = 1.0;

    for (int i = 1; i <= n; ++i)
      tree[i] = new ArrayList<>();

    for (int[] edge : edges) {
      final int u = edge[0];
      final int v = edge[1];
      tree[u].add(v);
      tree[v].add(u);
    }

    while (!q.isEmpty() && t-- > 0)
      for (int sz = q.size(); sz > 0; --sz) {
        final int a = q.poll();
        int nChildren = 0;
        for (final int b : tree[a])
          if (!seen[b])
            ++nChildren;
        for (final int b : tree[a]) {
          if (seen[b])
            continue;
          seen[b] = true;
          prob[b] = prob[a] / nChildren;
          q.add(b);
        }
        if (nChildren > 0)
          prob[a] = 0.0;
      }

    return prob[target];
  }
}

class Solution {
 public:
  double frogPosition(int n, vector<vector<int>>& edges, int t, int target) {
    vector<vector<int>> tree(n + 1);
    queue<int> q{{1}};
    vector<bool> seen(n + 1);
    vector<double> prob(n + 1);

    seen[1] = true;
    prob[1] = 1.0;

    for (const vector<int>& edge : edges) {
      const int u = edge[0];
      const int v = edge[1];
      tree[u].push_back(v);
      tree[v].push_back(u);
    }

    while (!q.empty() && t-- > 0)
      for (int sz = q.size(); sz > 0; --sz) {
        const int a = q.front();
        q.pop();
        const int nChildren =
            ranges::count_if(tree[a], [&seen](int b) { return !seen[b]; });
        for (const int b : tree[a]) {
          if (seen[b])
            continue;
          seen[b] = true;
          prob[b] = prob[a] / nChildren;
          q.push(b);
        }
        if (nChildren > 0)
          prob[a] = 0.0;
      }

    return prob[target];
  }
};


You are given an alphanumeric string `s`. (Alphanumeric string is a string
consisting of lowercase English letters and digits).

You have to find a permutation of the string where no letter is followed by
another letter and no digit is followed by another digit. That is, no two
adjacent characters have the same type.

Return the reformatted string or return an empty string if it is impossible to
reformat the string.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def reformat(self, s: str) -> str:
    A=[]
    for c in s:
      if c.isalpha():
        A.append(c)
    B=[]
    for c in s:
      if c.isdigit():
        B.append(c)

    if len(A) < len(B):
      A, B = B, A
    if len(A) - len(B) > 1:
      return ''

    ans = []

    for i in range(len(B)):
      ans.append(A[i])
      ans.append(B[i])

    if len(A) == len(B) + 1:
      ans.append(A[-1])
    return ''.join(ans)

class Solution {
  public String reformat(String s) {
    List<Character> A = new ArrayList<>();
    List<Character> B = new ArrayList<>();

    for (final char c : s.toCharArray())
      if (Character.isAlphabetic(c))
        A.add(c);
      else
        B.add(c);

    if (A.size() < B.size()) {
      List<Character> temp = A;
      A = B;
      B = temp;
    }
    if (A.size() - B.size() > 1)
      return "";

    StringBuilder sb = new StringBuilder();

    for (int i = 0; i < B.size(); ++i) {
      sb.append(A.get(i));
      sb.append(B.get(i));
    }

    if (A.size() > B.size())
      sb.append(A.get(A.size() - 1));
    return sb.toString();
  }
}

class Solution {
 public:
  string reformat(string s) {
    vector<char> A;
    vector<char> B;

    for (const char c : s)
      isalpha(c) ? A.push_back(c) : B.push_back(c);

    if (A.size() < B.size())
      swap(A, B);
    if (A.size() - B.size() > 1)
      return "";

    string ans;

    for (int i = 0; i < B.size(); ++i)
      ans += string{A[i], B[i]};

    if (A.size() > B.size())
      ans += A.back();
    return ans;
  }
};


There are a total of `numCourses` courses you have to take, labeled from `0`
to `numCourses - 1`. You are given an array `prerequisites` where
`prerequisites[i] = [ai, bi]` indicates that you must take course `ai` first
if you want to take course `bi`.

* For example, the pair `[0, 1]` indicates that you have to take course `0` before you can take course `1`.

Prerequisites can also be indirect. If course `a` is a prerequisite of course
`b`, and course `b` is a prerequisite of course `c`, then course `a` is a
prerequisite of course `c`.

You are also given an array `queries` where `queries[j] = [uj, vj]`. For the
`jth` query, you should answer whether course `uj` is a prerequisite of course
`vj` or not.

Return a boolean array `answer`, where `answer[j]` is the answer to the `jth`
query.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def checkIfPrerequisite(self, numCourses: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]:
    graph = [[] for _ in range(numCourses)]
    isPrerequisite = [[False] * numCourses for _ in range(numCourses)]

    for u, v in prerequisites:
      graph[u].append(v)

    for i in range(numCourses):
      self._dfs(graph, i, isPrerequisite[i])

    return [isPrerequisite[u][v] for u, v in queries]

  def _dfs(self, graph: List[List[int]], u: int, used: List[bool]) -> None:
    for v in graph[u]:
      if used[v]:
        continue
      used[v] = True
      self._dfs(graph, v, used)

class Solution {
  public List<Boolean> checkIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) {
    List<Boolean> ans = new ArrayList<>();
    List<Integer>[] graph = new List[numCourses];

    for (int i = 0; i < numCourses; ++i)
      graph[i] = new ArrayList<>();

    for (int[] prerequisite : prerequisites) {
      final int u = prerequisite[0];
      final int v = prerequisite[1];
      graph[u].add(v);
    }

    // isPrerequisite[i][j] := true if course i is a prerequisite of course j
    boolean[][] isPrerequisite = new boolean[numCourses][numCourses];

    // DFS from every course.
    for (int i = 0; i < numCourses; ++i)
      dfs(graph, i, isPrerequisite[i]);

    for (int[] query : queries) {
      final int u = query[0];
      final int v = query[1];
      ans.add(isPrerequisite[u][v]);
    }

    return ans;
  }

  public void dfs(List<Integer>[] graph, int u, boolean[] used) {
    for (final int v : graph[u]) {
      if (used[v])
        continue;
      used[v] = true;
      dfs(graph, v, used);
    }
  }
}

class Solution {
 public:
  vector<bool> checkIfPrerequisite(int numCourses,
                                   vector<vector<int>>& prerequisites,
                                   vector<vector<int>>& queries) {
    vector<bool> ans;
    vector<vector<int>> graph(numCourses);
    // isPrerequisite[i][j] := true if course i is a prerequisite of course j
    vector<vector<bool>> isPrerequisite(numCourses, vector<bool>(numCourses));

    for (const vector<int>& prerequisite : prerequisites) {
      const int u = prerequisite[0];
      const int v = prerequisite[1];
      graph[u].push_back(v);
    }

    // DFS from every course.
    for (int i = 0; i < numCourses; ++i)
      dfs(graph, i, isPrerequisite[i]);

    for (const vector<int>& query : queries) {
      const int u = query[0];
      const int v = query[1];
      ans.push_back(isPrerequisite[u][v]);
    }

    return ans;
  }

  void dfs(const vector<vector<int>>& graph, int u, vector<bool>& used) {
    for (const int v : graph[u]) {
      if (used[v])
        continue;
      used[v] = true;
      dfs(graph, v, used);
    }
  }
};


Find Critical and Pseudo-Critical Edges in Minimum Spanning Tree

Given a weighted undirected connected graph with `n` vertices numbered from
`0` to `n - 1`, and an array `edges` where `edges[i] = [ai, bi, weighti]`
represents a bidirectional and weighted edge between nodes `ai` and `bi`. A
minimum spanning tree (MST) is a subset of the graph's edges that connects all
vertices without cycles and with the minimum possible total edge weight.

Find all the critical and pseudo-critical edges in the given graph's minimum
spanning tree (MST). An MST edge whose deletion from the graph would cause the
MST weight to increase is called a critical edge. On the other hand, a pseudo-
critical edge is that which can appear in some MSTs but not all.

Note that you can return the indices of the edges in any order.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator, Union

class UnionFind:
  def __init__(self, n: int):
    self.id = list(range(n))
    self.rank = [0] * n

  def unionByRank(self, u: int, v: int) -> None:
    i = self.find(u)
    j = self.find(v)
    if i == j:
      return
    if self.rank[i] < self.rank[j]:
      self.id[i] = j
    elif self.rank[i] > self.rank[j]:
      self.id[j] = i
    else:
      self.id[i] = j
      self.rank[j] += 1

  def find(self, u: int) -> int:
    if self.id[u] != u:
      self.id[u] = self.find(self.id[u])
    return self.id[u]

class Solution:
  def findCriticalAndPseudoCriticalEdges(self, n: int, edges: List[List[int]]) -> List[List[int]]:
    criticalEdges = []
    pseudoCriticalEdges = []

    for i in range(len(edges)):
      edges[i].append(i)

    edges.sort(key=lambda x: x[2])

    def getMSTWeight(firstEdge: List[int], deletedEdgeIndex: int) -> Union[int, float]:
      mstWeight = 0
      uf = UnionFind(n)

      if firstEdge:
        uf.unionByRank(firstEdge[0], firstEdge[1])
        mstWeight += firstEdge[2]

      for u, v, weight, index in edges:
        if index == deletedEdgeIndex:
          continue
        if uf.find(u) == uf.find(v):
          continue
        uf.unionByRank(u, v)
        mstWeight += weight

      root = uf.find(0)
      if any(uf.find(i) != root for i in range(n)):
        return math.inf

      return mstWeight

    mstWeight = getMSTWeight([], -1)

    for edge in edges:
      index = edge[3]
      if getMSTWeight([], index) > mstWeight:
        criticalEdges.append(index)
      elif getMSTWeight(edge, -1) == mstWeight:
        pseudoCriticalEdges.append(index)

    return [criticalEdges, pseudoCriticalEdges]

class UnionFind {
  public UnionFind(int n) {
    id = new int[n];
    rank = new int[n];
    for (int i = 0; i < n; ++i)
      id[i] = i;
  }

  public void unionByRank(int u, int v) {
    final int i = find(u);
    final int j = find(v);
    if (i == j)
      return;
    if (rank[i] < rank[j]) {
      id[i] = j;
    } else if (rank[i] > rank[j]) {
      id[j] = i;
    } else {
      id[i] = j;
      ++rank[j];
    }
  }

  public int find(int u) {
    return id[u] == u ? u : (id[u] = find(id[u]));
  }

  private int[] id;
  private int[] rank;
}

class Solution {
  public List<List<Integer>> findCriticalAndPseudoCriticalEdges(int n, int[][] edges) {
    List<Integer> criticalEdges = new ArrayList<>();
    List<Integer> pseudoCriticalEdges = new ArrayList<>();

    // Record the index information, so edges[i] := (u, v, weight, index).
    for (int i = 0; i < edges.length; ++i)
      edges[i] = new int[] {edges[i][0], edges[i][1], edges[i][2], i};

    // Sort by the weight.
    Arrays.sort(edges, Comparator.comparingInt(edge -> edge[2]));

    final int mstWeight = getMSTWeight(n, edges, new int[] {}, -1);

    for (int[] edge : edges) {
      final int index = edge[3];
      // Deleting the `edge` increases the MST's weight or makes the MST
      // invalid.
      if (getMSTWeight(n, edges, new int[] {}, index) > mstWeight)
        criticalEdges.add(index);
      // If an edge can be in any MST, we can always add `edge` to the edge set.
      else if (getMSTWeight(n, edges, edge, -1) == mstWeight)
        pseudoCriticalEdges.add(index);
    }

    return List.of(criticalEdges, pseudoCriticalEdges);
  }

  private int getMSTWeight(int n, int[][] edges, int[] firstEdge, int deletedEdgeIndex) {
    int mstWeight = 0;
    UnionFind uf = new UnionFind(n);

    if (firstEdge.length == 4) {
      uf.unionByRank(firstEdge[0], firstEdge[1]);
      mstWeight += firstEdge[2];
    }

    for (int[] edge : edges) {
      final int u = edge[0];
      final int v = edge[1];
      final int weight = edge[2];
      final int index = edge[3];
      if (index == deletedEdgeIndex)
        continue;
      if (uf.find(u) == uf.find(v))
        continue;
      uf.unionByRank(u, v);
      mstWeight += weight;
    }

    final int root = uf.find(0);
    for (int i = 0; i < n; ++i)
      if (uf.find(i) != root)
        return Integer.MAX_VALUE;

    return mstWeight;
  }
}

class UnionFind {
 public:
  UnionFind(int n) : id(n), rank(n) {
    iota(id.begin(), id.end(), 0);
  }

  void unionByRank(int u, int v) {
    const int i = find(u);
    const int j = find(v);
    if (i == j)
      return;
    if (rank[i] < rank[j]) {
      id[i] = j;
    } else if (rank[i] > rank[j]) {
      id[j] = i;
    } else {
      id[i] = j;
      ++rank[j];
    }
  }

  int find(int u) {
    return id[u] == u ? u : id[u] = find(id[u]);
  }

 private:
  vector<int> id;
  vector<int> rank;
};

class Solution {
 public:
  vector<vector<int>> findCriticalAndPseudoCriticalEdges(
      int n, vector<vector<int>>& edges) {
    vector<int> criticalEdges;
    vector<int> pseudoCriticalEdges;

    // Record the index information, so edges[i] := (u, v, weight, index).
    for (int i = 0; i < edges.size(); ++i)
      edges[i].push_back(i);

    // Sort by the weight.
    ranges::sort(edges, ranges::less{},
                 [](const vector<int>& edge) { return edge[2]; });

    const int mstWeight = getMSTWeight(n, edges, {}, -1);

    for (const vector<int>& edge : edges) {
      const int index = edge[3];
      // Deleting the `edge` increases the MST's weight or makes the MST
      // invalid.
      if (getMSTWeight(n, edges, {}, index) > mstWeight)
        criticalEdges.push_back(index);
      // If an edge can be in any MST, we can always add `edge` to the edge set.
      else if (getMSTWeight(n, edges, edge, -1) == mstWeight)
        pseudoCriticalEdges.push_back(index);
    }

    return {criticalEdges, pseudoCriticalEdges};
  }

 private:
  int getMSTWeight(int n, const vector<vector<int>>& edges,
                   const vector<int>& firstEdge, int deletedEdgeIndex) {
    int mstWeight = 0;
    UnionFind uf(n);

    if (!firstEdge.empty()) {
      uf.unionByRank(firstEdge[0], firstEdge[1]);
      mstWeight += firstEdge[2];
    }

    for (const vector<int>& edge : edges) {
      const int u = edge[0];
      const int v = edge[1];
      const int weight = edge[2];
      const int index = edge[3];
      if (index == deletedEdgeIndex)
        continue;
      if (uf.find(u) == uf.find(v))
        continue;
      uf.unionByRank(u, v);
      mstWeight += weight;
    }

    const int root = uf.find(0);
    for (int i = 0; i < n; ++i)
      if (uf.find(i) != root)
        return INT_MAX;

    return mstWeight;
  }
};


Given a binary string `s`, you can split `s` into 3 non-empty strings `s1`,
`s2`, and `s3` where `s1 + s2 + s3 = s`.

Return the number of ways `s` can be split such that the number of ones is the
same in `s1`, `s2`, and `s3`. Since the answer may be too large, return it
modulo `109 + 7`.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def numWays(self, s: str) -> int:
    kMod = 1_000_000_007
    ones = s.count('1')
    if ones % 3 != 0:
      return 0
    if ones == 0:
      n = len(s)
      return (n - 1) * (n - 2) // 2 % kMod

    s1End = -1
    s2Start = -1
    s2End = -1
    s3Start = -1
    onesSoFar = 0

    for i, c in enumerate(s):
      if c == '1':
        onesSoFar += 1
      if s1End == -1 and onesSoFar == ones // 3:
        s1End = i
      elif s2Start == -1 and onesSoFar == ones // 3 + 1:
        s2Start = i
      if s2End == -1 and onesSoFar == ones // 3 * 2:
        s2End = i
      elif s3Start == -1 and onesSoFar == ones // 3 * 2 + 1:
        s3Start = i

    return (s2Start - s1End) * (s3Start - s2End) % kMod

class Solution {
  public int numWays(String s) {
    final int MOD = 1_000_000_007;
    final int ones = (int) s.chars().filter(c -> c == '1').count();
    if (ones % 3 != 0)
      return 0;
    if (ones == 0) {
      final long n = s.length();
      return (int) ((n - 1) * (n - 2) / 2 % MOD);
    }

    int s1End = -1;
    int s2Start = -1;
    int s2End = -1;
    int s3Start = -1;
    int onesSoFar = 0;

    for (int i = 0; i < s.length(); ++i) {
      if (s.charAt(i) == '1')
        ++onesSoFar;
      if (s1End == -1 && onesSoFar == ones / 3)
        s1End = i;
      else if (s2Start == -1 && onesSoFar == ones / 3 + 1)
        s2Start = i;
      if (s2End == -1 && onesSoFar == ones / 3 * 2)
        s2End = i;
      else if (s3Start == -1 && onesSoFar == ones / 3 * 2 + 1)
        s3Start = i;
    }

    return (int) ((long) (s2Start - s1End) * (s3Start - s2End) % MOD);
  }
}

class Solution {
 public:
  int numWays(string s) {
    constexpr int kMod = 1'000'000'007;
    const int ones = ranges::count(s, '1');
    if (ones % 3 != 0)
      return 0;
    if (ones == 0) {
      const long n = s.size();
      return (n - 1) * (n - 2) / 2 % kMod;
    }

    int s1End = -1;
    int s2Start = -1;
    int s2End = -1;
    int s3Start = -1;
    int onesSoFar = 0;

    for (int i = 0; i < s.length(); ++i) {
      if (s[i] == '1')
        ++onesSoFar;
      if (s1End == -1 && onesSoFar == ones / 3)
        s1End = i;
      else if (s2Start == -1 && onesSoFar == ones / 3 + 1)
        s2Start = i;
      if (s2End == -1 && onesSoFar == ones / 3 * 2)
        s2End = i;
      else if (s3Start == -1 && onesSoFar == ones / 3 * 2 + 1)
        s3Start = i;
    }

    return static_cast<long>(s2Start - s1End) * (s3Start - s2End) % kMod;
  }
};


Shortest Subarray to be Removed to Make Array Sorted

Given an integer array `arr`, remove a subarray (can be empty) from `arr` such
that the remaining elements in `arr` are non-decreasing.

Return the length of the shortest subarray to remove.

A subarray is a contiguous subsequence of the array.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def findLengthOfShortestSubarray(self, arr: List[int]) -> int:
    n = len(arr)
    l = 0
    r = n - 1

    while l < n - 1 and arr[l + 1] >= arr[l]:
      l += 1
    while r > 0 and arr[r - 1] <= arr[r]:
      r -= 1
    ans = min(n - 1 - l, r)

    i = l
    j = n - 1
    while i >= 0 and j >= r and j > i:
      if arr[i] <= arr[j]:
        j -= 1
      else:
        i -= 1
      ans = min(ans, j - i)

    return ans

class Solution {
  public int findLengthOfShortestSubarray(int[] arr) {
    final int n = arr.length;
    int l = 0;
    int r = n - 1;

    // arr[0..l] is non-decreasing prefix.
    while (l < n - 1 && arr[l + 1] >= arr[l])
      ++l;
    // arr[r..n - 1] is non-decreasing suffix.
    while (r > 0 && arr[r - 1] <= arr[r])
      --r;
    // Remove arr[l + 1..n - 1] or arr[0..r - 1]
    int ans = Math.min(n - 1 - l, r);

    // Since arr[0..l] and arr[r..n - 1] are non-decreasing, we place pointers
    // at the rightmost indices, l and n - 1, and greedily shrink them toward
    // the leftmost indices, 0 and r, respectively. By removing arr[i + 1..j],
    // we ensure that `arr` becomes non-decreasing.
    int i = l;
    int j = n - 1;
    while (i >= 0 && j >= r && j > i) {
      if (arr[i] <= arr[j])
        --j;
      else
        --i;
      ans = Math.min(ans, j - i);
    }

    return ans;
  }
}

class Solution {
 public:
  int findLengthOfShortestSubarray(vector<int>& arr) {
    const int n = arr.size();
    int l = 0;
    int r = n - 1;

    // arr[0..l] is non-decreasing.
    while (l < n - 1 && arr[l + 1] >= arr[l])
      ++l;
    // arr[r..n - 1] is non-decreasing.
    while (r > 0 && arr[r - 1] <= arr[r])
      --r;
    // Remove arr[l + 1..n - 1] or arr[0..r - 1].
    int ans = min(n - 1 - l, r);

    // Since arr[0..l] and arr[r..n - 1] are non-decreasing, we place pointers
    // at the rightmost indices, l and n - 1, and greedily shrink them toward
    // the leftmost indices, 0 and r, respectively. By removing arr[i + 1..j],
    // we ensure that `arr` becomes non-decreasing.
    int i = l;
    int j = n - 1;
    while (i >= 0 && j >= r && j > i) {
      if (arr[i] <= arr[j])
        --j;
      else
        --i;
      ans = min(ans, j - i);
    }

    return ans;
  }
};


Remove Max Number of Edges to Keep Graph Fully Traversable

Alice and Bob have an undirected graph of `n` nodes and three types of edges:

* Type 1: Can be traversed by Alice only.
* Type 2: Can be traversed by Bob only.
* Type 3: Can be traversed by both Alice and Bob.

Given an array `edges` where `edges[i] = [typei, ui, vi]` represents a
bidirectional edge of type `typei` between nodes `ui` and `vi`, find the
maximum number of edges you can remove so that after removing the edges, the
graph can still be fully traversed by both Alice and Bob. The graph is fully
traversed by Alice and Bob if starting from any node, they can reach all other
nodes.

Return the maximum number of edges you can remove, or return `-1` if Alice and
Bob cannot fully traverse the graph.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class UnionFind:
  def __init__(self, n: int):
    self.count = n
    self.id = list(range(n))
    self.rank = [0] * n

  def unionByRank(self, u: int, v: int) -> bool:
    i = self._find(u)
    j = self._find(v)
    if i == j:
      return False
    if self.rank[i] < self.rank[j]:
      self.id[i] = j
    elif self.rank[i] > self.rank[j]:
      self.id[j] = i
    else:
      self.id[i] = j
      self.rank[j] += 1
    self.count -= 1
    return True

  def _find(self, u: int) -> int:
    if self.id[u] != u:
      self.id[u] = self._find(self.id[u])
    return self.id[u]

class Solution:
  def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:
    alice = UnionFind(n)
    bob = UnionFind(n)
    requiredEdges = 0

    for type, u, v in sorted(edges, reverse=True):
      u -= 1
      v -= 1
      if type == 3:
        if alice.unionByRank(u, v) | bob.unionByRank(u, v):
          requiredEdges += 1
      elif type == 2:
        if bob.unionByRank(u, v):
          requiredEdges += 1
      else:
        if alice.unionByRank(u, v):
          requiredEdges += 1

    if alice.count == 1 and bob.count == 1:
        return len(edges) - requiredEdges
    else:
        return -1

class UnionFind {
  public UnionFind(int n) {
    count = n;
    id = new int[n];
    rank = new int[n];
    for (int i = 0; i < n; ++i)
      id[i] = i;
  }

  public boolean unionByRank(int u, int v) {
    final int i = find(u);
    final int j = find(v);
    if (i == j)
      return false;
    if (rank[i] < rank[j]) {
      id[i] = j;
    } else if (rank[i] > rank[j]) {
      id[j] = i;
    } else {
      id[i] = j;
      ++rank[j];
    }
    --count;
    return true;
  }

  public int getCount() {
    return count;
  }

  private int count;
  private int[] id;
  private int[] rank;

  private int find(int u) {
    return id[u] == u ? u : (id[u] = find(id[u]));
  }
}

class Solution {
  public int maxNumEdgesToRemove(int n, int[][] edges) {
    UnionFind alice = new UnionFind(n);
    UnionFind bob = new UnionFind(n);
    int requiredEdges = 0;

    // Greedily put type 3 edges in the front.
    Arrays.sort(edges, Comparator.comparingInt(edge -> - edge[0]));

    for (int[] edge : edges) {
      final int type = edge[0];
      final int u = edge[1] - 1;
      final int v = edge[2] - 1;
      switch (type) {
        case 3: // Can be traversed by Alice and Bob.
          // Note that we should use | instead of || because if the first
          // expression is true, short-circuiting will skip the second
          // expression.
          if (alice.unionByRank(u, v) | bob.unionByRank(u, v))
            ++requiredEdges;
          break;
        case 2: // Can be traversed by Bob.
          if (bob.unionByRank(u, v))
            ++requiredEdges;
          break;
        case 1: // Can be traversed by Alice.
          if (alice.unionByRank(u, v))
            ++requiredEdges;
      }
    }

    return alice.getCount() == 1 && bob.getCount() == 1 ? edges.length - requiredEdges : -1;
  }
}

class UnionFind {
 public:
  UnionFind(int n) : count(n), id(n), rank(n) {
    iota(id.begin(), id.end(), 0);
  }

  bool unionByRank(int u, int v) {
    const int i = find(u);
    const int j = find(v);
    if (i == j)
      return false;
    if (rank[i] < rank[j]) {
      id[i] = j;
    } else if (rank[i] > rank[j]) {
      id[j] = i;
    } else {
      id[i] = j;
      ++rank[j];
    }
    --count;
    return true;
  }

  int getCount() const {
    return count;
  }

 private:
  int count;
  vector<int> id;
  vector<int> rank;

  int find(int u) {
    return id[u] == u ? u : id[u] = find(id[u]);
  }
};

class Solution {
 public:
  int maxNumEdgesToRemove(int n, vector<vector<int>>& edges) {
    UnionFind alice(n);
    UnionFind bob(n);
    int requiredEdges = 0;

    // Greedily put type 3 edges in the front.
    ranges::sort(edges, ranges::greater{},
                 [](const vector<int>& edge) { return edge[0]; });

    for (const vector<int>& edge : edges) {
      const int type = edge[0];
      const int u = edge[1] - 1;
      const int v = edge[2] - 1;
      switch (type) {
        case 3:  // Can be traversed by Alice and Bob.
          // Note that we should use | instead of || because if the first
          // expression is true, short-circuiting will skip the second
          // expression.
          if (alice.unionByRank(u, v) | bob.unionByRank(u, v))
            ++requiredEdges;
          break;
        case 2:  // Can be traversed by Bob.
          if (bob.unionByRank(u, v))
            ++requiredEdges;
          break;
        case 1:  // Can be traversed by Alice.
          if (alice.unionByRank(u, v))
            ++requiredEdges;
          break;
      }
    }

    return alice.getCount() == 1 && bob.getCount() == 1
               ? edges.size() - requiredEdges
               : -1;
  }
};


Given an `m x n` binary matrix `mat`, return the number of special positions
in `mat`.

A position `(i, j)` is called special if `mat[i][j] == 1` and all other
elements in row `i` and column `j` are `0` (rows and columns are 0-indexed).

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def numSpecial(self, mat: List[List[int]]) -> int:
    m = len(mat)
    n = len(mat[0])
    ans = 0
    rowOnes = [0] * m
    colOnes = [0] * n

    for i in range(m):
      for j in range(n):
        if mat[i][j] == 1:
          rowOnes[i] += 1
          colOnes[j] += 1

    for i in range(m):
      for j in range(n):
        if mat[i][j] == 1 and rowOnes[i] == 1 and colOnes[j] == 1:
          ans += 1

    return ans

class Solution {
  public int numSpecial(int[][] mat) {
    final int m = mat.length;
    final int n = mat[0].length;
    int ans = 0;
    int[] rowOnes = new int[m];
    int[] colOnes = new int[n];

    for (int i = 0; i < m; ++i)
      for (int j = 0; j < n; ++j)
        if (mat[i][j] == 1) {
          ++rowOnes[i];
          ++colOnes[j];
        }

    for (int i = 0; i < m; ++i)
      for (int j = 0; j < n; ++j)
        if (mat[i][j] == 1 && rowOnes[i] == 1 && colOnes[j] == 1)
          ++ans;

    return ans;
  }
}

class Solution {
 public:
  int numSpecial(vector<vector<int>>& mat) {
    const int m = mat.size();
    const int n = mat[0].size();
    int ans = 0;
    vector<int> rowOnes(m);
    vector<int> colOnes(n);

    for (int i = 0; i < m; ++i)
      for (int j = 0; j < n; ++j)
        if (mat[i][j] == 1) {
          ++rowOnes[i];
          ++colOnes[j];
        }

    for (int i = 0; i < m; ++i)
      for (int j = 0; j < n; ++j)
        if (mat[i][j] == 1 && rowOnes[i] == 1 && colOnes[j] == 1)
          ++ans;

    return ans;
  }
};


You are given a list of `preferences` for `n` friends, where `n` is always
even.

For each person `i`, `preferences[i]` contains a list of friends sorted in the
order of preference. In other words, a friend earlier in the list is more
preferred than a friend later in the list. Friends in each list are denoted by
integers from `0` to `n-1`.

All the friends are divided into pairs. The pairings are given in a list
`pairs`, where `pairs[i] = [xi, yi]` denotes `xi` is paired with `yi` and `yi`
is paired with `xi`.

However, this pairing may cause some of the friends to be unhappy. A friend
`x` is unhappy if `x` is paired with `y` and there exists a friend `u` who is
paired with `v` but:

* `x` prefers `u` over `y`, and
* `u` prefers `x` over `v`.

Return the number of unhappy friends.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def unhappyFriends(self, n: int, preferences: List[List[int]], pairs: List[List[int]]) -> int:
    ans = 0
    matches = [0] * n
    prefer = [{} for _ in range(n)]

    for x, y in pairs:
      matches[x] = y
      matches[y] = x

    for i in range(n):
      for j in range(n - 1):
        prefer[i][preferences[i][j]] = j

    for x in range(n):
      for u in prefer[x].keys():
        y = matches[x]
        v = matches[u]
        if prefer[x][u] < prefer[x][y] and prefer[u][x] < prefer[u][v]:
          ans += 1
          break

    return ans

class Solution {
  public int unhappyFriends(int n, int[][] preferences, int[][] pairs) {
    int ans = 0;
    int[] matches = new int[n];
    Map<Integer, Integer>[] prefer = new Map[n];

    for (int[] pair : pairs) {
      final int x = pair[0];
      final int y = pair[1];
      matches[x] = y;
      matches[y] = x;
    }

    for (int i = 0; i < n; ++i)
      prefer[i] = new HashMap<>();

    for (int i = 0; i < n; ++i)
      for (int j = 0; j < n - 1; ++j)
        prefer[i].put(preferences[i][j], j);

    for (int x = 0; x < n; ++x)
      for (final int u : preferences[x]) {
        final int y = matches[x];
        final int v = matches[u];
        if (prefer[x].get(u) < prefer[x].get(y) && prefer[u].get(x) < prefer[u].get(v)) {
          ++ans;
          break;
        }
      }

    return ans;
  }
}

class Solution {
 public:
  int unhappyFriends(int n, vector<vector<int>>& preferences,
                     vector<vector<int>>& pairs) {
    int ans = 0;
    vector<int> matches(n);
    vector<unordered_map<int, int>> prefer(n);

    for (const vector<int>& pair : pairs) {
      const int x = pair[0];
      const int y = pair[1];
      matches[x] = y;
      matches[y] = x;
    }

    for (int i = 0; i < n; ++i)
      for (int j = 0; j < n - 1; ++j)
        prefer[i][preferences[i][j]] = j;

    for (int x = 0; x < n; ++x)
      for (const auto& [u, _] : prefer[x]) {
        const int y = matches[x];
        const int v = matches[u];
        if (prefer[x][u] < prefer[x][y] && prefer[u][x] < prefer[u][v]) {
          ++ans;
          break;
        }
      }

    return ans;
  }
};


There is a strange printer with the following two special requirements:

* On each turn, the printer will print a solid rectangular pattern of a single color on the grid. This will cover up the existing colors in the rectangle.
* Once the printer has used a color for the above operation, the same color cannot be used again.

You are given a `m x n` matrix `targetGrid`, where `targetGrid[row][col]` is
the color in the position `(row, col)` of the grid.

Return `true` if it is possible to print the matrix `targetGrid`, otherwise,
return `false`.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

from enum import Enum

class State(Enum):
  kInit = 0
  kVisiting = 1
  kVisited = 2

class Solution:
  def isPrintable(self, targetGrid: List[List[int]]) -> bool:
    kMaxColor = 60
    m = len(targetGrid)
    n = len(targetGrid[0])

    graph = [set() for _ in range(kMaxColor + 1)]

    for color in range(1, kMaxColor + 1):
      minI = m
      minJ = n
      maxI = -1
      maxJ = -1
      for i in range(m):
        for j in range(n):
          if targetGrid[i][j] == color:
            minI = min(minI, i)
            minJ = min(minJ, j)
            maxI = max(maxI, i)
            maxJ = max(maxJ, j)

      for i in range(minI, maxI + 1):
        for j in range(minJ, maxJ + 1):
          if targetGrid[i][j] != color:
            graph[color].add(targetGrid[i][j])

    states = [State.kInit] * (kMaxColor + 1)

    def hasCycle(u: int) -> bool:
      if states[u] == State.kVisiting:
        return True
      if states[u] == State.kVisited:
        return False

      states[u] = State.kVisiting
      if any(hasCycle(v) for v in graph[u]):
        return True
      states[u] = State.kVisited

      return False

    for i in range(1, kMaxColor + 1):
      if hasCycle(i):
        return False
    return True

enum State { INIT, VISITING, VISITED }

class Solution {
  public boolean isPrintable(int[][] targetGrid) {
    final int MAX_COLOR = 60;
    final int m = targetGrid.length;
    final int n = targetGrid[0].length;
    // graph[u] := {v1, v2} means v1 and v2 cover u
    Set<Integer>[] graph = new HashSet[MAX_COLOR + 1];

    for (int color = 1; color <= MAX_COLOR; ++color) {
      // Get the rectangle of the current color.
      int minI = m;
      int minJ = n;
      int maxI = -1;
      int maxJ = -1;
      for (int i = 0; i < m; ++i)
        for (int j = 0; j < n; ++j)
          if (targetGrid[i][j] == color) {
            minI = Math.min(minI, i);
            minJ = Math.min(minJ, j);
            maxI = Math.max(maxI, i);
            maxJ = Math.max(maxJ, j);
          }
      // Add any color covering the current as the children.
      graph[color] = new HashSet<>();
      for (int i = minI; i <= maxI; ++i)
        for (int j = minJ; j <= maxJ; ++j)
          if (targetGrid[i][j] != color) {
            graph[color].add(targetGrid[i][j]);
          }
    }

    State[] states = new State[MAX_COLOR + 1];

    for (int color = 1; color <= MAX_COLOR; ++color)
      if (hasCycle(graph, color, states))
        return false;

    return true;
  }

  private boolean hasCycle(Set<Integer>[] graph, int u, State[] states) {
    if (states[u] == State.VISITING)
      return true;
    if (states[u] == State.VISITED)
      return false;
    states[u] = State.VISITING;
    for (int v : graph[u])
      if (hasCycle(graph, v, states))
        return true;
    states[u] = State.VISITED;
    return false;
  }
}

enum class State { kInit, kVisiting, kVisited };

class Solution {
 public:
  bool isPrintable(vector<vector<int>>& targetGrid) {
    constexpr int kMaxColor = 60;
    const int m = targetGrid.size();
    const int n = targetGrid[0].size();
    // graph[u] := {v1, v2} means v1 and v2 cover u
    vector<unordered_set<int>> graph(kMaxColor + 1);

    for (int color = 1; color <= kMaxColor; ++color) {
      // Get the rectangle of the current color.
      int minI = m;
      int minJ = n;
      int maxI = -1;
      int maxJ = -1;
      for (int i = 0; i < m; ++i)
        for (int j = 0; j < n; ++j)
          if (targetGrid[i][j] == color) {
            minI = min(minI, i);
            minJ = min(minJ, j);
            maxI = max(maxI, i);
            maxJ = max(maxJ, j);
          }
      // Add any color covering the current as the children.
      for (int i = minI; i <= maxI; ++i)
        for (int j = minJ; j <= maxJ; ++j)
          if (targetGrid[i][j] != color)
            graph[color].insert(targetGrid[i][j]);
    }

    vector<State> states(kMaxColor + 1);

    for (int color = 1; color <= kMaxColor; ++color)
      if (hasCycle(graph, color, states))
        return false;

    return true;
  }

 private:
  bool hasCycle(const vector<unordered_set<int>>& graph, int u,
                vector<State>& states) {
    if (states[u] == State::kVisiting)
      return true;
    if (states[u] == State::kVisited)
      return false;
    states[u] = State::kVisiting;
    for (const int v : graph[u])
      if (hasCycle(graph, v, states))
        return true;
    states[u] = State::kVisited;
    return false;
  }
};


Alert Using Same Key-Card Three or More Times in a One Hour Period

LeetCode company workers use key-cards to unlock office doors. Each time a
worker uses their key-card, the security system saves the worker's name and
the time when it was used. The system emits an alert if any worker uses the
key-card three or more times in a one-hour period.

You are given a list of strings `keyName` and `keyTime` where `[keyName[i],
keyTime[i]]` corresponds to a person's name and the time when their key-card
was used in a single day.

Access times are given in the 24-hour time format "HH:MM", such as `"23:51"`
and `"09:49"`.

Return a list of unique worker names who received an alert for frequent
keycard use. Sort the names in ascending order alphabetically.

Notice that `"10:00"` \- `"11:00"` is considered to be within a one-hour
period, while `"22:51"` \- `"23:52"` is not considered to be within a one-hour
period.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def alertNames(self, keyName: List[str], keyTime: List[str]) -> List[str]:
    nameToMinutes = collections.defaultdict(list)

    for name, time in zip(keyName, keyTime):
      minutes = self._getMinutes(time)
      nameToMinutes[name].append(minutes)

    res=[]
    for name, minutes in nameToMinutes.items():
      if self._hasAlert(minutes):
        res.append(name)
    return sorted(res)

  def _hasAlert(self, minutes: List[int]) -> bool:
    if len(minutes) > 70:
      return True
    minutes.sort()
    for i in range(2, len(minutes)):
      if minutes[i - 2] + 60 >= minutes[i]:
        return True
    return False

  def _getMinutes(self, time: str) -> int:
    h, m = map(int, time.split(':'))
    return 60 * h + m

class Solution {
  public List<String> alertNames(String[] keyName, String[] keyTime) {
    List<String> ans = new ArrayList<>();
    HashMap<String, List<Integer>> nameToMinutes = new HashMap<>();

    for (int i = 0; i < keyName.length; i++) {
      final int minutes = getMinutes(keyTime[i]);
      nameToMinutes.putIfAbsent(keyName[i], new ArrayList<>());
      nameToMinutes.get(keyName[i]).add(minutes);
    }

    for (Map.Entry<String, List<Integer>> entry : nameToMinutes.entrySet()) {
      final String name = entry.getKey();
      List<Integer> minutes = entry.getValue();
      if (hasAlert(minutes))
        ans.add(name);
    }

    Collections.sort(ans);
    return ans;
  }

  private boolean hasAlert(List<Integer> minutes) {
    if (minutes.size() > 70)
      return true;
    Collections.sort(minutes);
    for (int i = 2; i < minutes.size(); i++)
      if (minutes.get(i - 2) + 60 >= minutes.get(i))
        return true;
    return false;
  }

  private int getMinutes(String time) {
    final int h = Integer.parseInt(time.substring(0, 2));
    final int m = Integer.parseInt(time.substring(3));
    return 60 * h + m;
  }
}

class Solution {
 public:
  vector<string> alertNames(vector<string>& keyName, vector<string>& keyTime) {
    vector<string> ans;
    unordered_map<string, vector<int>> nameToMinutes;

    for (int i = 0; i < keyName.size(); ++i) {
      const int minutes = getMinutes(keyTime[i]);
      nameToMinutes[keyName[i]].push_back(minutes);
    }

    for (auto& [name, minutes] : nameToMinutes)
      if (hasAlert(minutes))
        ans.push_back(name);

    ranges::sort(ans);
    return ans;
  }

 private:
  // Returns true if any worker uses the key-card three or more times in an
  // one-hour period.
  bool hasAlert(vector<int>& minutes) {
    if (minutes.size() > 70)
      return true;
    ranges::sort(minutes);
    for (int i = 2; i < minutes.size(); ++i)
      if (minutes[i - 2] + 60 >= minutes[i])
        return true;
    return false;
  }

  int getMinutes(const string& time) {
    const int h = stoi(time.substr(0, 2));
    const int m = stoi(time.substr(3));
    return 60 * h + m;
  }
};


There is an infrastructure of `n` cities with some number of `roads`
connecting these cities. Each `roads[i] = [ai, bi]` indicates that there is a
bidirectional road between cities `ai` and `bi`.

The network rank of two different cities is defined as the total number of
directly connected roads to either city. If a road is directly connected to
both cities, it is only counted once.

The maximal network rank of the infrastructure is the maximum network rank of
all pairs of different cities.

Given the integer `n` and the array `roads`, return the maximal network rank
of the entire infrastructure.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def maximalNetworkRank(self, n: int, roads: List[List[int]]) -> int:
    degrees = [0] * n

    for u, v in roads:
      degrees[u] += 1
      degrees[v] += 1

    maxDegree1 = 0
    maxDegree2 = 0
    for degree in degrees:
      if degree > maxDegree1:
        maxDegree2 = maxDegree1
        maxDegree1 = degree
      elif degree > maxDegree2:
        maxDegree2 = degree

    countMaxDegree1 = 0
    countMaxDegree2 = 0
    for degree in degrees:
      if degree == maxDegree1:
        countMaxDegree1 += 1
      elif degree == maxDegree2:
        countMaxDegree2 += 1

    if countMaxDegree1 == 1:
      edgeCount = self._getEdgeCount(roads, degrees, maxDegree1, maxDegree2) + self._getEdgeCount(roads, degrees, maxDegree2, maxDegree1)
      return maxDegree1 + maxDegree2 - (countMaxDegree2 == edgeCount)
    else:
      edgeCount = self._getEdgeCount(roads, degrees, maxDegree1, maxDegree1)
      maxPossibleEdgeCount = countMaxDegree1 * (countMaxDegree1 - 1) // 2
      return 2 * maxDegree1 - (maxPossibleEdgeCount == edgeCount)

  def _getEdgeCount(self, roads: List[List[int]], degrees: List[int], degreeU: int, degreeV: int) -> int:
    edgeCount = 0
    for u, v in roads:
      if degrees[u] == degreeU and degrees[v] == degreeV:
        edgeCount += 1
    return edgeCount

class Solution {
  public int maximalNetworkRank(int n, int[][] roads) {
    int[] degrees = new int[n];

    for (int[] road : roads) {
      final int u = road[0];
      final int v = road[1];
      ++degrees[u];
      ++degrees[v];
    }

    // Find the first maximum and the second maximum degrees.
    int maxDegree1 = 0;
    int maxDegree2 = 0;
    for (final int degree : degrees)
      if (degree > maxDegree1) {
        maxDegree2 = maxDegree1;
        maxDegree1 = degree;
      } else if (degree > maxDegree2) {
        maxDegree2 = degree;
      }

    // There can be multiple nodes with `maxDegree1` or `maxDegree2`.
    // Find the counts of such nodes.
    int countMaxDegree1 = 0;
    int countMaxDegree2 = 0;
    for (final int degree : degrees)
      if (degree == maxDegree1)
        ++countMaxDegree1;
      else if (degree == maxDegree2)
        ++countMaxDegree2;

    if (countMaxDegree1 == 1) {
      // 1. If there is only one node with degree = `maxDegree1`, then we'll
      // need to use the node with degree = `maxDegree2`. The answer in general
      // will be (maxDegree1 + maxDegree2), but if the two nodes that we're
      // considering are connected, then we'll have to subtract 1.
      final int edgeCount = getEdgeCount(roads, degrees, maxDegree1, maxDegree2) +
                            getEdgeCount(roads, degrees, maxDegree2, maxDegree1);
      return maxDegree1 + maxDegree2 - (countMaxDegree2 == edgeCount ? 1 : 0);
    } else {
      // 2. If there are more than one node with degree = `maxDegree1`, then we
      // can consider `maxDegree1` twice, and we don't need to use `maxDegree2`.
      // The answer in general will be 2 * maxDegree1, but if the two nodes that
      // we're considering are connected, then we'll have to subtract 1.
      final int edgeCount = getEdgeCount(roads, degrees, maxDegree1, maxDegree1);
      final int maxPossibleEdgeCount = countMaxDegree1 * (countMaxDegree1 - 1) / 2;
      return 2 * maxDegree1 - (maxPossibleEdgeCount == edgeCount ? 1 : 0);
    }
  }

  // Returns the number of edges (u, v) where degress[u] == degreeU and
  // degrees[v] == degreeV.
  private int getEdgeCount(int[][] roads, int[] degrees, int degreeU, int degreeV) {
    int edgeCount = 0;
    for (int[] road : roads) {
      final int u = road[0];
      final int v = road[1];
      if (degrees[u] == degreeU && degrees[v] == degreeV)
        ++edgeCount;
    }
    return edgeCount;
  }
}

class Solution {
 public:
  int maximalNetworkRank(int n, vector<vector<int>>& roads) {
    vector<int> degrees(n);

    for (const vector<int>& road : roads) {
      const int u = road[0];
      const int v = road[1];
      ++degrees[u];
      ++degrees[v];
    }

    // Find the first maximum and the second maximum degrees.
    int maxDegree1 = 0;
    int maxDegree2 = 0;
    for (const int degree : degrees) {
      if (degree > maxDegree1) {
        maxDegree2 = maxDegree1;
        maxDegree1 = degree;
      } else if (degree > maxDegree2) {
        maxDegree2 = degree;
      }
    }

    // There can be multiple nodes with `maxDegree1` or `maxDegree2`.
    // Find the counts of such nodes.
    int countMaxDegree1 = 0;
    int countMaxDegree2 = 0;
    for (const int degree : degrees)
      if (degree == maxDegree1)
        ++countMaxDegree1;
      else if (degree == maxDegree2)
        ++countMaxDegree2;

    if (countMaxDegree1 == 1) {
      // 1. If there is only one node with degree = `maxDegree1`, then we'll
      // need to use the node with degree = `maxDegree2`. The answer in general
      // will be (maxDegree1 + maxDegree2), but if the two nodes that we're
      // considering are connected, then we'll have to subtract 1.
      const int edgeCount =
          getEdgeCount(roads, degrees, maxDegree1, maxDegree2) +
          getEdgeCount(roads, degrees, maxDegree2, maxDegree1);
      return maxDegree1 + maxDegree2 - (countMaxDegree2 == edgeCount ? 1 : 0);
    } else {
      // 2. If there are more than one node with degree = `maxDegree1`, then we
      // can consider `maxDegree1` twice, and we don't need to use `maxDegree2`.
      // The answer in general will be 2 * maxDegree1, but if the two nodes that
      // we're considering are connected, then we'll have to subtract 1.
      const int edgeCount =
          getEdgeCount(roads, degrees, maxDegree1, maxDegree1);
      const int maxPossibleEdgeCount =
          countMaxDegree1 * (countMaxDegree1 - 1) / 2;
      return 2 * maxDegree1 - (maxPossibleEdgeCount == edgeCount ? 1 : 0);
    }
  }

 private:
  // Returns the number of edges (u, v) where degress[u] == degreeU and
  // degrees[v] == degreeV.
  int getEdgeCount(const vector<vector<int>>& roads, const vector<int>& degrees,
                   int degreeU, int degreeV) {
    int edgeCount = 0;
    for (const vector<int>& road : roads) {
      const int u = road[0];
      const int v = road[1];
      if (degrees[u] == degreeU && degrees[v] == degreeV)
        ++edgeCount;
    }
    return edgeCount;
  }
};


You are given two strings `a` and `b` of the same length. Choose an index and
split both strings at the same index, splitting `a` into two strings:
`aprefix` and `asuffix` where `a = aprefix + asuffix`, and splitting `b` into
two strings: `bprefix` and `bsuffix` where `b = bprefix + bsuffix`. Check if
`aprefix + bsuffix` or `bprefix + asuffix` forms a palindrome.

When you split a string `s` into `sprefix` and `ssuffix`, either `ssuffix` or
`sprefix` is allowed to be empty. For example, if `s = "abc"`, then `"" +
"abc"`, `"a" + "bc"`, `"ab" + "c"` , and `"abc" + ""` are valid splits.

Return `true` if it is possible to form a palindrome string, otherwise return
`false`.

Notice that `x + y` denotes the concatenation of strings `x` and `y`.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def checkPalindromeFormation(self, a: str, b: str) -> bool:
    return self._check(a, b) or self._check(b, a)

  def _check(self, a: str, b: str) -> bool:
    i, j = 0, len(a) - 1
    while i < j:
      if a[i] != b[j]:
        return self._isPalindrome(a, i, j) or self._isPalindrome(b, i, j)
      i += 1
      j -= 1
    return True

  def _isPalindrome(self, s: str, i: int, j: int) -> bool:
    while i < j:
      if s[i] != s[j]:
        return False
      i += 1
      j -= 1
    return True

class Solution {
  public boolean checkPalindromeFormation(String a, String b) {
    return check(a, b) || check(b, a);
  }

  private boolean check(String a, String b) {
    for (int i = 0, j = a.length() - 1; i < j; ++i, --j)
      if (a.charAt(i) != b.charAt(j))
        // a[0:i] + a[i..j] + b[j + 1:] or
        // a[0:i] + b[i..j] + b[j + 1:]
        return isPalindrome(a, i, j) || isPalindrome(b, i, j);
    return true;
  }

  private boolean isPalindrome(String s, int i, int j) {
    while (i < j)
      if (s.charAt(i++) != s.charAt(j--))
        return false;
    return true;
  }
}

class Solution {
 public:
  bool checkPalindromeFormation(string a, string b) {
    return check(a, b) || check(b, a);
  }

 private:
  bool check(const string& a, const string& b) {
    for (int i = 0, j = a.length() - 1; i < j; ++i, --j)
      if (a[i] != b[j])
        // a[0:i] + a[i..j] + b[j + 1:] or
        // a[0:i] + b[i..j] + b[j + 1:]
        return isPalindrome(a, i, j) || isPalindrome(b, i, j);
    return true;
  }

  bool isPalindrome(const string& s, int i, int j) {
    while (i < j)
      if (s[i++] != s[j--])
        return false;
    return true;
  }
};


Count Subtrees With Max Distance Between Cities

There are `n` cities numbered from `1` to `n`. You are given an array `edges`
of size `n-1`, where `edges[i] = [ui, vi]` represents a bidirectional edge
between cities `ui` and `vi`. There exists a unique path between each pair of
cities. In other words, the cities form a tree.

A subtree is a subset of cities where every city is reachable from every other
city in the subset, where the path between each pair passes through only the
cities from the subset. Two subtrees are different if there is a city in one
subtree that is not present in the other.

For each `d` from `1` to `n-1`, find the number of subtrees in which the
maximum distance between any two cities in the subtree is equal to `d`.

Return an array of size `n-1` where the `dth` element (1-indexed) is the
number of subtrees in which the maximum distance between any two cities is
equal to `d`.

Notice that the distance between the two cities is the number of edges in the
path between them.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def countSubgraphsForEachDiameter(self, n: int, edges: List[List[int]]) -> List[int]:
    maxMask = 1 << n
    dist = self._floydWarshall(n, edges)
    ans = [0] * (n - 1)

    for mask in range(maxMask):
      maxDist = self._getMaxDist(mask, dist, n)
      if maxDist > 0:
        ans[maxDist - 1] += 1

    return ans

  def _floydWarshall(self, n: int, edges: List[List[int]]) -> List[List[int]]:
    dist = [[n] * n for _ in range(n)]

    for i in range(n):
      dist[i][i] = 0

    for u, v in edges:
      dist[u - 1][v - 1] = 1
      dist[v - 1][u - 1] = 1

    for k in range(n):
      for i in range(n):
        for j in range(n):
          dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])

    return dist

  def _getMaxDist(self, mask: int, dist: List[List[int]], n: int) -> int:
    maxDist = 0
    edgeCount = 0
    cityCount = 0
    for u in range(n):
      if (mask >> u) & 1 == 0:
        continue
      cityCount += 1
      for v in range(u + 1, n):
        if (mask >> v) & 1 == 0:
          continue
        if dist[u][v] == 1:
          edgeCount += 1
        maxDist = max(maxDist, dist[u][v])

    if edgeCount == cityCount - 1:
      return maxDist
    else:
      return 0

class Solution {
  public int[] countSubgraphsForEachDiameter(int n, int[][] edges) {
    final int maxMask = 1 << n;
    final int[][] dist = floydWarshall(n, edges);
    int[] ans = new int[n - 1];

    // mask := the subset of the cities
    for (int mask = 0; mask < maxMask; ++mask) {
      int maxDist = getMaxDist(mask, dist, n);
      if (maxDist > 0)
        ++ans[maxDist - 1];
    }

    return ans;
  }

  private int[][] floydWarshall(int n, int[][] edges) {
    int[][] dist = new int[n][n];
    for (int i = 0; i < n; ++i)
      Arrays.fill(dist[i], n);

    for (int i = 0; i < n; ++i)
      dist[i][i] = 0;

    for (int[] edge : edges) {
      final int u = edge[0] - 1;
      final int v = edge[1] - 1;
      dist[u][v] = 1;
      dist[v][u] = 1;
    }

    for (int k = 0; k < n; ++k)
      for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
          dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);

    return dist;
  }

  private int getMaxDist(int mask, int[][] dist, int n) {
    int maxDist = 0;
    int edgeCount = 0;
    int cityCount = 0;
    for (int u = 0; u < n; ++u) {
      if ((mask >> u & 1) == 0) // u is not in the subset.
        continue;
      ++cityCount;
      for (int v = u + 1; v < n; ++v) {
        if ((mask >> v & 1) == 0) // v is not in the subset.
          continue;
        if (dist[u][v] == 1) // u and v are connected.
          ++edgeCount;
        maxDist = Math.max(maxDist, dist[u][v]);
      }
    }
    return edgeCount == cityCount - 1 ? maxDist : 0;
  }
}

class Solution {
 public:
  vector<int> countSubgraphsForEachDiameter(int n, vector<vector<int>>& edges) {
    const int maxMask = 1 << n;
    const vector<vector<int>> dist = floydWarshall(n, edges);
    vector<int> ans(n - 1);

    // mask := the subset of the cities
    for (int mask = 0; mask < maxMask; ++mask) {
      const int maxDist = getMaxDist(mask, dist, n);
      if (maxDist > 0)
        ++ans[maxDist - 1];
    }

    return ans;
  }

 private:
  vector<vector<int>> floydWarshall(int n, const vector<vector<int>>& edges) {
    vector<vector<int>> dist(n, vector<int>(n, n));

    for (int i = 0; i < n; ++i)
      dist[i][i] = 0;

    for (const vector<int>& edge : edges) {
      const int u = edge[0] - 1;
      const int v = edge[1] - 1;
      dist[u][v] = 1;
      dist[v][u] = 1;
    }

    for (int k = 0; k < n; ++k)
      for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
          dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);

    return dist;
  }

  int getMaxDist(int mask, const vector<vector<int>>& dist, int n) {
    int maxDist = 0;
    int edgeCount = 0;
    int cityCount = 0;
    for (int u = 0; u < n; ++u) {
      if ((mask >> u & 1) == 0)  // u is not in the subset.
        continue;
      ++cityCount;
      for (int v = u + 1; v < n; ++v) {
        if ((mask >> v & 1) == 0)  // v is not in the subset.
          continue;
        if (dist[u][v] == 1)  // u and v are connected.
          ++edgeCount;
        maxDist = max(maxDist, dist[u][v]);
      }
    }
    return edgeCount == cityCount - 1 ? maxDist : 0;
  }
};


We have `n` cities labeled from `1` to `n`. Two different cities with labels
`x` and `y` are directly connected by a bidirectional road if and only if `x`
and `y` share a common divisor strictly greater than some `threshold`. More
formally, cities with labels `x` and `y` have a road between them if there
exists an integer `z` such that all of the following are true:

* `x % z == 0`,
* `y % z == 0`, and
* `z > threshold`.

Given the two integers, `n` and `threshold`, and an array of `queries`, you
must determine for each `queries[i] = [ai, bi]` if cities `ai` and `bi` are
connected directly or indirectly. (i.e. there is some path between them).

Return an array `answer`, where `answer.length == queries.length` and
`answer[i]` is `true` if for the `ith` query, there is a path between `ai` and
`bi`, or `answer[i]` is `false` if there is no path.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class UnionFind:
  def __init__(self, n: int):
    self.id = list(range(n))
    self.rank = [0] * n

  def unionByRank(self, u: int, v: int) -> bool:
    i = self.find(u)
    j = self.find(v)
    if i == j:
      return False
    if self.rank[i] < self.rank[j]:
      self.id[i] = j
    elif self.rank[i] > self.rank[j]:
      self.id[j] = i
    else:
      self.id[i] = j
      self.rank[j] += 1
    return True

  def find(self, u: int) -> int:
    if self.id[u] != u:
      self.id[u] = self.find(self.id[u])
    return self.id[u]

class Solution:
  def areConnected(self, n: int, threshold: int, queries: List[List[int]]) -> List[bool]:
    uf = UnionFind(n + 1)

    for z in range(threshold + 1, n + 1):
      for x in range(z * 2, n + 1, z):
        uf.unionByRank(z, x)

    return [uf.find(a) == uf.find(b) for a, b in queries]

class UnionFind {
  public UnionFind(int n) {
    id = new int[n];
    rank = new int[n];
    for (int i = 0; i < n; ++i)
      id[i] = i;
  }

  public boolean unionByRank(int u, int v) {
    final int i = find(u);
    final int j = find(v);
    if (i == j)
      return false;
    if (rank[i] < rank[j]) {
      id[i] = j;
    } else if (rank[i] > rank[j]) {
      id[j] = i;
    } else {
      id[i] = j;
      ++rank[j];
    }
    return true;
  }

  public int find(int u) {
    return id[u] == u ? u : (id[u] = find(id[u]));
  }

  private int[] id;
  private int[] rank;
}

class Solution {
  public List<Boolean> areConnected(int n, int threshold, int[][] queries) {
    List<Boolean> ans = new ArrayList<>();
    UnionFind uf = new UnionFind(n + 1);

    for (int z = threshold + 1; z <= n; ++z)
      for (int x = z * 2; x <= n; x += z)
        uf.unionByRank(z, x);

    for (int[] query : queries) {
      final int a = query[0];
      final int b = query[1];
      ans.add(uf.find(a) == uf.find(b));
    }

    return ans;
  }
}

class UnionFind {
 public:
  UnionFind(int n) : id(n), rank(n) {
    iota(id.begin(), id.end(), 0);
  }

  bool unionByRank(int u, int v) {
    const int i = find(u);
    const int j = find(v);
    if (i == j)
      return false;
    if (rank[i] < rank[j]) {
      id[i] = j;
    } else if (rank[i] > rank[j]) {
      id[j] = i;
    } else {
      id[i] = j;
      ++rank[j];
    }
    return true;
  }

  int find(int u) {
    return id[u] == u ? u : id[u] = find(id[u]);
  }

 private:
  vector<int> id;
  vector<int> rank;
};

class Solution {
 public:
  vector<bool> areConnected(int n, int threshold,
                            vector<vector<int>>& queries) {
    vector<bool> ans;
    UnionFind uf(n + 1);

    for (int z = threshold + 1; z <= n; ++z)
      for (int x = z * 2; x <= n; x += z)
        uf.unionByRank(z, x);

    for (const vector<int>& query : queries) {
      const int a = query[0];
      const int b = query[1];
      ans.push_back(uf.find(a) == uf.find(b));
    }

    return ans;
  }
};


You are a hiker preparing for an upcoming hike. You are given `heights`, a 2D
array of size `rows x columns`, where `heights[row][col]` represents the
height of cell `(row, col)`. You are situated in the top-left cell, `(0, 0)`,
and you hope to travel to the bottom-right cell, `(rows-1, columns-1)` (i.e.,
0-indexed). You can move up, down, left, or right, and you wish to find a
route that requires the minimum effort.

A route's effort is the maximum absolute difference in heights between two
consecutive cells of the route.

Return the minimum effort required to travel from the top-left cell to the
bottom-right cell.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def minimumEffortPath(self, heights: List[List[int]]) -> int:
    dirs = ((0, 1), (1, 0), (0, -1), (-1, 0))
    m = len(heights)
    n = len(heights[0])
    diff = [[math.inf] * n for _ in range(m)]
    seen = set()

    minHeap = [(0, 0, 0)]
    diff[0][0] = 0

    while minHeap:
      d, i, j = heapq.heappop(minHeap)
      if i == m - 1 and j == n - 1:
        return d
      seen.add((i, j))
      for dx, dy in dirs:
        x = i + dx
        y = j + dy
        if x < 0 or x == m or y < 0 or y == n:
          continue
        if (x, y) in seen:
          continue
        newDiff = abs(heights[i][j] - heights[x][y])
        maxDiff = max(diff[i][j], newDiff)
        if diff[x][y] > maxDiff:
          diff[x][y] = maxDiff
          heapq.heappush(minHeap, (diff[x][y], x, y))

class Solution {
  public int minimumEffortPath(int[][] heights) {
    // d := the maximum difference of (i, j) and its neighbors
    record T(int i, int j, int d) {}
    final int[][] DIRS = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    final int m = heights.length;
    final int n = heights[0].length;
    Queue<T> minHeap = new PriorityQueue<>(Comparator.comparingInt(T::d));
    // dist[i][j] := the maximum absolute difference to reach (i, j)
    int[][] diff = new int[m][n];
    Arrays.stream(diff).forEach(A -> Arrays.fill(A, Integer.MAX_VALUE));
    boolean[][] seen = new boolean[m][n];

    minHeap.offer(new T(0, 0, 0));
    diff[0][0] = 0;

    while (!minHeap.isEmpty()) {
      final int i = minHeap.peek().i;
      final int j = minHeap.peek().j;
      final int d = minHeap.poll().d;
      if (i == m - 1 && j == n - 1)
        return d;
      seen[i][j] = true;
      for (int[] dir : DIRS) {
        final int x = i + dir[0];
        final int y = j + dir[1];
        if (x < 0 || x == m || y < 0 || y == n)
          continue;
        if (seen[x][y])
          continue;
        final int newDiff = Math.abs(heights[i][j] - heights[x][y]);
        final int maxDiff = Math.max(diff[i][j], newDiff);
        if (diff[x][y] > maxDiff) {
          diff[x][y] = maxDiff;
          minHeap.offer(new T(x, y, maxDiff));
        }
      }
    }

    throw new IllegalArgumentException();
  }
}

struct T {
  int i;
  int j;
  int d;  // the maximum difference of (i, j) and its neighbors
};

class Solution {
 public:
  int minimumEffortPath(vector<vector<int>>& heights) {
    constexpr int kDirs[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    const int m = heights.size();
    const int n = heights[0].size();
    auto compare = [](const T& a, const T& b) { return a.d > b.d; };
    priority_queue<T, vector<T>, decltype(compare)> minHeap(compare);
    // diff[i][j] := the maximum absolute difference to reach (i, j)
    vector<vector<int>> diff(m, vector<int>(n, INT_MAX));
    vector<vector<bool>> seen(m, vector<bool>(n));

    minHeap.emplace(0, 0, 0);
    diff[0][0] = 0;

    while (!minHeap.empty()) {
      const auto [i, j, d] = minHeap.top();
      minHeap.pop();
      if (i == m - 1 && j == n - 1)
        return d;
      seen[i][j] = true;
      for (const auto& [dx, dy] : kDirs) {
        const int x = i + dx;
        const int y = j + dy;
        if (x < 0 || x == m || y < 0 || y == n)
          continue;
        if (seen[x][y])
          continue;
        const int newDiff = abs(heights[i][j] - heights[x][y]);
        const int maxDiff = max(diff[i][j], newDiff);
        if (diff[x][y] > maxDiff) {
          diff[x][y] = maxDiff;
          minHeap.emplace(x, y, maxDiff);
        }
      }
    }

    throw;
  }
};


Given an `m x n` `matrix`, return a new matrix `answer` where
`answer[row][col]` is the rank of `matrix[row][col]`.

The rank is an integer that represents how large an element is compared to
other elements. It is calculated using the following rules:

* The rank is an integer starting from `1`.
* If two elements `p` and `q` are in the same row or column, then:
* If `p < q` then `rank(p) < rank(q)`
* If `p == q` then `rank(p) == rank(q)`
* If `p > q` then `rank(p) > rank(q)`
* The rank should be as small as possible.

The test cases are generated so that `answer` is unique under the given rules.

import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class UnionFind:
  def __init__(self):
    self.id = {}

  def union(self, u: int, v: int) -> None:
    self.id.setdefault(u, u)
    self.id.setdefault(v, v)
    i = self._find(u)
    j = self._find(v)
    if i != j:
      self.id[i] = j

  def getGroupIdToValues(self) -> Dict[int, List[int]]:
    groupIdToValues = collections.defaultdict(list)
    for u in self.id.keys():
      groupIdToValues[self._find(u)].append(u)
    return groupIdToValues

  def _find(self, u: int) -> int:
    if self.id[u] != u:
      self.id[u] = self._find(self.id[u])
    return self.id[u]

class Solution:
  def matrixRankTransform(self, matrix: List[List[int]]) -> List[List[int]]:
    m = len(matrix)
    n = len(matrix[0])
    ans = [[0] * n for _ in range(m)]
    valToGrids = collections.defaultdict(list)
    maxRankSoFar = [0] * (m + n)

    for i, row in enumerate(matrix):
      for j, val in enumerate(row):
        valToGrids[val].append((i, j))

    for _, grids in sorted(valToGrids.items()):
      uf = UnionFind()
      for i, j in grids:
        uf.union(i, j + m)
      for values in uf.getGroupIdToValues().values():
        maxRank = max(maxRankSoFar[i] for i in values)
        for i in values:
          maxRankSoFar[i] = maxRank + 1
      for i, j in grids:
        ans[i][j] = maxRankSoFar[i]

    return ans

class UnionFind {
  public void union(int u, int v) {
    id.putIfAbsent(u, u);
    id.putIfAbsent(v, v);
    final int i = find(u);
    final int j = find(v);
    if (i != j)
      id.put(i, j);
  }

  public Map<Integer, List<Integer>> getGroupIdToValues() {
    Map<Integer, List<Integer>> groupIdToValues = new HashMap<>();
    for (Map.Entry<Integer, Integer> entry : id.entrySet()) {
      final int u = entry.getKey();
      final int i = find(u);
      groupIdToValues.putIfAbsent(i, new ArrayList<>());
      groupIdToValues.get(i).add(u);
    }
    return groupIdToValues;
  }

  private Map<Integer, Integer> id = new HashMap<>();

  private int find(int u) {
    return id.getOrDefault(u, u) == u ? u : find(id.get(u));
  }
}

class Solution {
  public int[][] matrixRankTransform(int[][] matrix) {
    final int m = matrix.length;
    final int n = matrix[0].length;
    int[][] ans = new int[m][n];
    // {val: [(i, j)]}
    TreeMap<Integer, List<Pair<Integer, Integer>>> valToGrids = new TreeMap<>();
    // rank[i] := the maximum rank of the row or column so far
    int[] maxRankSoFar = new int[m + n];

    for (int i = 0; i < m; ++i)
      for (int j = 0; j < n; ++j) {
        final int val = matrix[i][j];
        valToGrids.putIfAbsent(val, new ArrayList<>());
        valToGrids.get(val).add(new Pair<>(i, j));
      }

    for (Map.Entry<Integer, List<Pair<Integer, Integer>>> entry : valToGrids.entrySet()) {
      final int val = entry.getKey();
      List<Pair<Integer, Integer>> grids = entry.getValue();
      UnionFind uf = new UnionFind();
      for (Pair<Integer, Integer> grid : grids) {
        final int i = grid.getKey();
        final int j = grid.getValue();
        // Union i-th row with j-th col.
        uf.union(i, j + m);
      }
      for (List<Integer> values : uf.getGroupIdToValues().values()) {
        // Get the maximum rank of all the included rows and columns.
        int maxRank = 0;
        for (final int i : values)
          maxRank = Math.max(maxRank, maxRankSoFar[i]);
        // Update all the rows and columns to maxRank + 1.
        for (final int i : values)
          maxRankSoFar[i] = maxRank + 1;
      }
      for (Pair<Integer, Integer> grid : grids) {
        final int i = grid.getKey();
        final int j = grid.getValue();
        ans[i][j] = maxRankSoFar[i];
      }
    }

    return ans;
  }
}

class UnionFind {
 public:
  void union_(int u, int v) {
    if (!id.contains(u))
      id[u] = u;
    if (!id.contains(v))
      id[v] = v;
    const int i = find(u);
    const int j = find(v);
    if (i != j)
      id[i] = j;
  }

  unordered_map<int, vector<int>> getGroupIdToValues() {
    unordered_map<int, vector<int>> groupIdToValues;
    for (const auto& [u, _] : id)
      groupIdToValues[find(u)].push_back(u);
    return groupIdToValues;
  }

 private:
  unordered_map<int, int> id;

  int find(int u) {
    return id[u] == u ? u : id[u] = find(id[u]);
  }
};

class Solution {
 public:
  vector<vector<int>> matrixRankTransform(vector<vector<int>>& matrix) {
    const int m = matrix.size();
    const int n = matrix[0].size();
    vector<vector<int>> ans(m, vector<int>(n));
    // {val: [(i, j)]}
    map<int, vector<pair<int, int>>> valToGrids;
    // rank[i] := the maximum rank of the row or column so far
    vector<int> maxRankSoFar(m + n);

    for (int i = 0; i < m; ++i)
      for (int j = 0; j < n; ++j)
        valToGrids[matrix[i][j]].emplace_back(i, j);

    for (const auto& [val, grids] : valToGrids) {
      UnionFind uf;
      for (const auto& [i, j] : grids)
        // Union i-th row with j-th col.
        uf.union_(i, j + m);
      for (const auto& [groupId, values] : uf.getGroupIdToValues()) {
        // Get the maximum rank of all the included rows and columns.
        int maxRank = 0;
        for (const int i : values)
          maxRank = max(maxRank, maxRankSoFar[i]);
        // Update all the rows and columns to maxRank + 1.
        for (const int i : values)
          maxRankSoFar[i] = maxRank + 1;
      }
      for (const auto& [i, j] : grids)
        ans[i][j] = maxRankSoFar[i];
    }

    return ans;
  }
};