Maximize Value of Function in a Ball Passing Game

You are given an integer array `receiver` of length `n` and an integer `k`.
`n` players are playing a ball-passing game.

You choose the starting player, `i`. The game proceeds as follows: player `i`
passes the ball to player `receiver[i]`, who then passes it to
`receiver[receiver[i]]`, and so on, for `k` passes in total. The game's score
is the sum of the indices of the players who touched the ball, including
repetitions, i.e. `i + receiver[i] + receiver[receiver[i]] + ... +
receiver(k)[i]`.

Return the maximum possible score.

Notes:

* `receiver` may contain duplicates.
* `receiver[i]` may be equal to `i`.

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 getMaxFunctionValue(self, receiver: List[int], k: int) -> int:
    n = len(receiver)
    m = int(math.log2(k)) + 1
    ans = 0
    jump = [[0] * m for _ in range(n)]
    summ = [[0] * m for _ in range(n)]

    for i in range(n):
      jump[i][0] = receiver[i]
      summ[i][0] = receiver[i]

    for j in range(1, m):
      for i in range(n):
        midNode = jump[i][j - 1]
        jump[i][j] = jump[midNode][j - 1]
        summ[i][j] = summ[i][j - 1] + summ[midNode][j - 1]

    for i in range(n):
      currSum = i
      currPos = i
      for j in range(m):
        if (k >> j) & 1 == 1:
          currSum += summ[currPos][j]
          currPos = jump[currPos][j]
      ans = max(ans, currSum)

    return ans

class Solution {
  public long getMaxFunctionValue(List<Integer> receiver, long k) {
    final int n = receiver.size();
    final int m = (int) (Math.log(k) / Math.log(2)) + 1;
    long ans = 0;
    // jump[i][j] := the the node you reach after jumping 2^j steps from i
    int[][] jump = new int[n][m];
    // sum[i][j] := the sum of the first 2^j nodes you reach when jumping from i
    long[][] sum = new long[n][m];

    for (int i = 0; i < n; ++i) {
      jump[i][0] = receiver.get(i);
      sum[i][0] = receiver.get(i);
    }

    // Calculate binary lifting.
    for (int j = 1; j < m; ++j)
      for (int i = 0; i < n; ++i) {
        final int midNode = jump[i][j - 1];
        //   the the node you reach after jumping 2^j steps from i
        // = the node you reach after jumping 2^(j - 1) steps from i
        // + the node you reach after jumping another 2^(j - 1) steps
        jump[i][j] = jump[midNode][j - 1];
        //   the sum of the first 2^j nodes you reach when jumping from i
        // = the sum of the first 2^(j - 1) nodes you reach when jumping from i
        // + the sum of another 2^(j - 1) nodes you reach
        sum[i][j] = sum[i][j - 1] + sum[midNode][j - 1];
      }

    for (int i = 0; i < n; ++i) {
      long currSum = i;
      int currPos = i;
      for (int j = 0; j < m; ++j)
        if ((k >> j & 1) == 1) {
          currSum += sum[currPos][j];
          currPos = jump[currPos][j];
        }
      ans = Math.max(ans, currSum);
    }

    return ans;
  }
}

class Solution {
 public:
  long long getMaxFunctionValue(vector<int>& receiver, long long k) {
    const int n = receiver.size();
    const int m = log2(k) + 1;
    long ans = 0;
    // jump[i][j] := the the node you reach after jumping 2^j steps from i
    vector<vector<int>> jump(n, vector<int>(m));
    // sum[i][j] := the sum of the first 2^j nodes you reach when jumping from i
    vector<vector<long>> sum(n, vector<long>(m));

    for (int i = 0; i < n; ++i) {
      jump[i][0] = receiver[i];
      sum[i][0] = receiver[i];
    }

    // Calculate binary lifting.
    for (int j = 1; j < m; ++j)
      for (int i = 0; i < n; ++i) {
        const int midNode = jump[i][j - 1];
        //   the the node you reach after jumping 2^j steps from i
        // = the node you reach after jumping 2^(j - 1) steps from i
        // + the node you reach after jumping another 2^(j - 1) steps
        jump[i][j] = jump[midNode][j - 1];
        //   the sum of the first 2^j nodes you reach when jumping from i
        // = the sum of the first 2^(j - 1) nodes you reach when jumping from i
        // + the sum of another 2^(j - 1) nodes you reach
        sum[i][j] = sum[i][j - 1] + sum[midNode][j - 1];
      }

    for (int i = 0; i < n; ++i) {
      long currSum = i;
      int currPos = i;
      for (int j = 0; j < m; ++j)
        if (k >> j & 1) {
          currSum += sum[currPos][j];
          currPos = jump[currPos][j];
        }
      ans = max(ans, currSum);
    }

    return ans;
  }
};


Minimum Operations to Make a Special Number

You are given a 0-indexed string `num` representing a non-negative integer.

In one operation, you can pick any digit of `num` and delete it. Note that if
you delete all the digits of `num`, `num` becomes `0`.

Return the minimum number of operations required to make `num` special.

An integer `x` is considered special if it is divisible by `25`.

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 minimumOperations(self, num: str) -> int:
    n = len(num)
    seenFive = False
    seenZero = False

    for i in range(n - 1, -1, -1):
      if seenZero and num[i] == '0':
        return n - i - 2
      if seenZero and num[i] == '5':
        return n - i - 2
      if seenFive and num[i] == '2':
        return n - i - 2
      if seenFive and num[i] == '7':
        return n - i - 2
      seenZero = seenZero or num[i] == '0'
      seenFive = seenFive or num[i] == '5'

    if seenZero:
      return n - 1
    else:
      return n

class Solution {
  public int minimumOperations(String num) {
    final int n = num.length();
    boolean seenFive = false;
    boolean seenZero = false;

    for (int i = n - 1; i >= 0; --i) {
      if (seenZero && num.charAt(i) == '0') // '00'
        return n - i - 2;
      if (seenZero && num.charAt(i) == '5') // '50'
        return n - i - 2;
      if (seenFive && num.charAt(i) == '2') // '25'
        return n - i - 2;
      if (seenFive && num.charAt(i) == '7') // '75'
        return n - i - 2;
      seenZero = seenZero || num.charAt(i) == '0';
      seenFive = seenFive || num.charAt(i) == '5';
    }

    return seenZero ? n - 1 : n;
  }
}

class Solution {
 public:
  int minimumOperations(string num) {
    const int n = num.length();
    bool seenFive = false;
    bool seenZero = false;

    for (int i = n - 1; i >= 0; --i) {
      if (seenZero && num[i] == '0')  // '00'
        return n - i - 2;
      if (seenZero && num[i] == '5')  // '50'
        return n - i - 2;
      if (seenFive && num[i] == '2')  // '25'
        return n - i - 2;
      if (seenFive && num[i] == '7')  // '75'
        return n - i - 2;
      seenZero = seenZero || num[i] == '0';
      seenFive = seenFive || num[i] == '5';
    }

    return seenZero ? n - 1 : n;
  }
};


Minimum Edge Weight Equilibrium Queries in a Tree

There is an undirected tree with `n` nodes labeled from `0` to `n - 1`. You
are given the integer `n` and a 2D integer array `edges` of length `n - 1`,
where `edges[i] = [ui, vi, wi]` indicates that there is an edge between nodes
`ui` and `vi` with weight `wi` in the tree.

You are also given a 2D integer array `queries` of length `m`, where
`queries[i] = [ai, bi]`. For each query, find the minimum number of operations
required to make the weight of every edge on the path from `ai` to `bi` equal.
In one operation, you can choose any edge of the tree and change its weight to
any value.

Note that:

* Queries are independent of each other, meaning that the tree returns to its initial state on each new query.
* The path from `ai` to `bi` is a sequence of distinct nodes starting with node `ai` and ending with node `bi` such that every two adjacent nodes in the sequence share an edge in the tree.

Return an array `answer` of length `m` where `answer[i]` is the answer to the
`ith` 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 minOperationsQueries(self, n: int, edges: List[List[int]], queries: List[List[int]]) -> List[int]:
    kMax = 26
    m = int(math.log2(n)) + 1
    ans = []
    graph = [[] for _ in range(n)]
    jump = [[0] * m for _ in range(n)]
    count = [[] for _ in range(n)]
    depth = [0] * n

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

    def dfs(u: int, prev: int, d: int):
      if prev != -1:
        jump[u][0] = prev
      depth[u] = d
      for v, w in graph[u]:
        if v == prev:
          continue
        count[v] = count[u][:]
        count[v][w] += 1
        dfs(v, u, d + 1)

    count[0] = [0] * (kMax + 1)
    dfs(0, -1, 0)

    for j in range(1, m):
      for i in range(n):
        jump[i][j] = jump[jump[i][j - 1]][j - 1]

    def getLCA(u: int, v: int) -> int:
      if depth[u] > depth[v]:
        return getLCA(v, u)
      for j in range(m):
        if depth[v] - depth[u] >> j & 1:
          v = jump[v][j]
      if u == v:
        return u
      for j in range(m - 1, -1, -1):
        if jump[u][j] != jump[v][j]:
          u = jump[u][j]
          v = jump[v][j]
      return jump[v][0]

    for u, v in queries:
      lca = getLCA(u, v)
      numEdges = depth[u] + depth[v] - 2 * depth[lca]
      maxFreq = max(count[u][j] + count[v][j] - 2 * count[lca][j] for j in range(1, kMax + 1))
      ans.append(numEdges - maxFreq)

    return ans

class Solution {
  public int[] minOperationsQueries(int n, int[][] edges, int[][] queries) {
    final int MAX = 26;
    final int m = (int) Math.ceil(Math.log(n) / Math.log(2));
    int[] ans = new int[queries.length];
    List<Pair<Integer, Integer>>[] graph = new List[n];
    // jump[i][j] := the 2^j-th ancestor of i
    int[][] jump = new int[n][m];
    // depth[i] := the depth of i
    int[] depth = new int[n];
    // count[i][j] := the count of j from root to i, where 1 <= j <= 26
    int[][] count = new int[n][MAX + 1];
    Arrays.setAll(graph, i -> new ArrayList<>());

    for (int[] edge : edges) {
      final int u = edge[0];
      final int v = edge[1];
      final int w = edge[2];
      graph[u].add(new Pair<>(v, w));
      graph[v].add(new Pair<>(u, w));
    }

    count[0] = new int[MAX + 1];
    dfs(graph, 0, /*prev=*/-1, jump, depth, count);

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

    for (int i = 0; i < queries.length; ++i) {
      final int u = queries[i][0];
      final int v = queries[i][1];
      final int lca = getLCA(u, v, jump, depth);
      // the number of edges between (u, v).
      final int numEdges = depth[u] + depth[v] - 2 * depth[lca];
      // the maximum frequency of edges between (u, v)
      int maxFreq = 0;
      for (int j = 1; j <= MAX; ++j)
        maxFreq = Math.max(maxFreq, count[u][j] + count[v][j] - 2 * count[lca][j]);
      ans[i] = numEdges - maxFreq;
    }

    return ans;
  }

  private void dfs(List<Pair<Integer, Integer>>[] graph, int u, int prev, int[][] jump, int[] depth,
                   int[][] count) {
    for (Pair<Integer, Integer> pair : graph[u]) {
      final int v = pair.getKey();
      final int w = pair.getValue();
      if (v == prev)
        continue;
      jump[v][0] = u;
      depth[v] = depth[u] + 1;
      count[v] = count[u].clone();
      ++count[v][w];
      dfs(graph, v, u, jump, depth, count);
    }
  }

  // Returns the lca(u, v) by binary jump.
  private int getLCA(int u, int v, int[][] jump, int[] depth) {
    // v is always deeper than u.
    if (depth[u] > depth[v])
      return getLCA(v, u, jump, depth);
    // Jump v to the same height of u.
    for (int j = 0; j < jump[0].length; ++j)
      if ((depth[v] - depth[u] >> j & 1) == 1)
        v = jump[v][j];
    if (u == v)
      return u;
    // Jump u and v to the node right below the lca.
    for (int j = jump[0].length - 1; j >= 0; --j)
      if (jump[u][j] != jump[v][j]) {
        u = jump[u][j];
        v = jump[v][j];
      }
    return jump[v][0];
  }
}

class Solution {
 public:
  vector<int> minOperationsQueries(int n, vector<vector<int>>& edges,
                                   vector<vector<int>>& queries) {
    constexpr int kMax = 26;
    const int m = ceil(log2(n));
    vector<int> ans;
    vector<vector<pair<int, int>>> graph(n);
    // jump[i][j] := the 2^j-th ancestor of i
    vector<vector<int>> jump(n, vector<int>(m));
    // depth[i] := the depth of i
    vector<int> depth(n);
    // count[i][j] := the count of j from root to i, where 1 <= j <= 26
    vector<vector<int>> count(n);

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

    count[0] = vector<int>(kMax + 1);
    dfs(graph, 0, /*prev=*/-1, jump, depth, count);

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

    for (const vector<int>& query : queries) {
      const int u = query[0];
      const int v = query[1];
      const int lca = getLCA(u, v, jump, depth);
      // the number of edges between (u, v).
      const int numEdges = depth[u] + depth[v] - 2 * depth[lca];
      // the maximum frequency of edges between (u, v)
      int maxFreq = 0;
      for (int j = 1; j <= kMax; ++j)
        maxFreq = max(maxFreq, count[u][j] + count[v][j] - 2 * count[lca][j]);
      ans.push_back(numEdges - maxFreq);
    }

    return ans;
  }

 private:
  void dfs(const vector<vector<pair<int, int>>>& graph, int u, int prev,
           vector<vector<int>>& jump, vector<int>& depth,
           vector<vector<int>>& count) {
    for (const auto& [v, w] : graph[u]) {
      if (v == prev)
        continue;
      jump[v][0] = u;
      depth[v] = depth[u] + 1;
      count[v] = count[u];
      ++count[v][w];
      dfs(graph, v, u, jump, depth, count);
    }
  }

  // Returns the lca(u, v) by binary jump.
  int getLCA(int u, int v, const vector<vector<int>>& jump,
             const vector<int>& depth) {
    // v is always deeper than u.
    if (depth[u] > depth[v])
      return getLCA(v, u, jump, depth);
    // Jump v to the same height of u.
    for (int j = 0; j < jump[0].size(); ++j)
      if (depth[v] - depth[u] >> j & 1)
        v = jump[v][j];
    if (u == v)
      return u;
    // Jump u and v to the node right below the lca.
    for (int j = jump[0].size() - 1; j >= 0; --j)
      if (jump[u][j] != jump[v][j]) {
        u = jump[u][j];
        v = jump[v][j];
      }
    return jump[v][0];
  }
};


Minimum Moves to Spread Stones Over Grid

You are given a 0-indexed 2D integer matrix `grid` of size `3 * 3`,
representing the number of stones in each cell. The grid contains exactly `9`
stones, and there can be multiple stones in a single cell.

In one move, you can move a single stone from its current cell to any other
cell if the two cells share a side.

Return the minimum number of moves required to place one stone in each 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 minimumMoves(self, grid: List[List[int]]) -> int:
    if sum(row.count(0) for row in grid) == 0:
      return 0

    ans = math.inf

    for i in range(3):
      for j in range(3):
        if grid[i][j] == 0:
          for x in range(3):
            for y in range(3):
              if grid[x][y] > 1:
                grid[x][y] -= 1
                grid[i][j] += 1
                ans = min(ans, abs(x - i) + abs(y - j) + self.minimumMoves(grid))
                grid[x][y] += 1
                grid[i][j] -= 1

    return ans

class Solution {
  public int minimumMoves(int[][] grid) {
    int zeroCount = 0;
    for (int[] row : grid)
      for (int cell : row)
        if (cell == 0)
          ++zeroCount;
    if (zeroCount == 0)
      return 0;

    int ans = Integer.MAX_VALUE;

    for (int i = 0; i < 3; ++i)
      for (int j = 0; j < 3; ++j)
        if (grid[i][j] == 0)
          for (int x = 0; x < 3; ++x)
            for (int y = 0; y < 3; ++y)
              if (grid[x][y] > 1) {
                --grid[x][y];
                ++grid[i][j];
                ans = Math.min(ans, Math.abs(x - i) + Math.abs(y - j) + minimumMoves(grid));
                ++grid[x][y];
                --grid[i][j];
              }

    return ans;
  }
}

class Solution {
 public:
  int minimumMoves(vector<vector<int>>& grid) {
    const int zeroCount = accumulate(grid.begin(), grid.end(), 0,
                                     [](int acc, const vector<int>& row) {
      return acc + ranges::count(row, 0);
    });
    if (zeroCount == 0)
      return 0;

    int ans = INT_MAX;

    for (int i = 0; i < 3; ++i)
      for (int j = 0; j < 3; ++j)
        if (grid[i][j] == 0)
          for (int x = 0; x < 3; ++x)
            for (int y = 0; y < 3; ++y)
              // Move a stone at (x, y) to (i, j).
              if (grid[x][y] > 1) {
                --grid[x][y];
                ++grid[i][j];
                ans = min(ans, abs(x - i) + abs(y - j) + minimumMoves(grid));
                ++grid[x][y];
                --grid[i][j];
              }

    return ans;
  }
};


You are given two strings `s` and `t` of equal length `n`. You can perform the
following operation on the string `s`:

* Remove a suffix of `s` of length `l` where `0 < l < n` and append it at the start of `s`.
For example, let `s = 'abcd'` then in one operation you can remove the suffix
`'cd'` and append it in front of `s` making `s = 'cdab'`.

You are also given an integer `k`. Return the number of ways in which `s` can
be transformed into `t` in exactly `k` operations.

Since the answer can be 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 numberOfWays(self, s: str, t: str, k: int) -> int:
    kMod = 1_000_000_007
    n = len(s)
    negOnePowK = 1 if k % 2 == 0 else -1  # (-1)^k
    z = self._zFunction(s + t + t)

    indices = [i - n for i in range(n, n + n) if z[i] >= n]
    dp = [0] * 2
    dp[1] = (pow(n - 1, k, kMod) - negOnePowK) * pow(n, kMod - 2, kMod)
    dp[0] = dp[1] + negOnePowK
    res = 0
    for index in indices:
      if index == 0:
        res += dp[0]
      else:
        res += dp[1]
    return res % kMod

  def _zFunction(self, s: str) -> List[int]:
    n = len(s)
    z = [0] * n
    l = 0
    r = 0
    for i in range(1, n):
      if i < r:
        z[i] = min(r - i, z[i - l])
      while i + z[i] < n and s[z[i]] == s[i + z[i]]:
        z[i] += 1
      if i + z[i] > r:
        l = i
        r = i + z[i]
    return z

class Solution {
  // This dynamic programming table dp[k][i] represents the number of ways to
  // rearrange the String s after k steps such that it starts with s[i].
  // A String can be rotated from 1 to n - 1 times. The transition rule is
  // dp[k][i] = sum(dp[k - 1][j]) for all j != i. For example, when n = 4 and
  // k = 3, the table looks like this:
  //
  // -----------------------------------------------------------
  // |       | i = 0 | i = 1 | i = 2 | i = 3 | sum = (n - 1)^k |
  // -----------------------------------------------------------
  // | k = 0 |   1   |   0   |   0   |   0   |        1        |
  // | k = 1 |   0   |   1   |   1   |   1   |        3        |
  // | k = 2 |   3   |   2   |   2   |   2   |        9        |
  // | k = 3 |   6   |   7   |   7   |   7   |       27        |
  // -----------------------------------------------------------
  //
  // By observation, we have
  //   * dp[k][!0] = ((n - 1)^k - (-1)^k) / n
  //   * dp[k][0] = dp[k][!0] + (-1)^k
  public int numberOfWays(String s, String t, long k) {
    final int n = s.length();
    final int negOnePowK = (k % 2 == 0 ? 1 : -1); // (-1)^k
    final int[] z = zFunction(s + t + t);
    final List<Integer> indices = getIndices(z, n);
    int[] dp = new int[2]; // dp[0] := dp[k][0]; dp[1] := dp[k][!0]
    dp[1] = (int) ((modPow(n - 1, k) - negOnePowK + MOD) % MOD * modPow(n, MOD - 2) % MOD);
    dp[0] = (int) ((dp[1] + negOnePowK + MOD) % MOD);
    int ans = 0;
    for (final int index : getIndices(z, n)) {
      ans += dp[index == 0 ? 0 : 1];
      ans %= MOD;
    }
    return ans;
  }

  private static final int MOD = 1_000_000_007;

  private long modPow(long x, long n) {
    if (n == 0)
      return 1;
    if (n % 2 == 1)
      return x * modPow(x, n - 1) % MOD;
    return modPow(x * x % MOD, n / 2);
  }

  // Returns the z array, where z[i] is the length of the longest prefix of
  // s[i..n) which is also a prefix of s.
  //
  // https://cp-algorithms.com/string/z-function.html#implementation
  private int[] zFunction(final String s) {
    final int n = s.length();
    int[] z = new int[n];
    int l = 0;
    int r = 0;
    for (int i = 1; i < n; ++i) {
      if (i < r)
        z[i] = Math.min(r - i, z[i - l]);
      while (i + z[i] < n && s.charAt(z[i]) == s.charAt(i + z[i]))
        ++z[i];
      if (i + z[i] > r) {
        l = i;
        r = i + z[i];
      }
    }
    return z;
  }

  // Returns the indices in `s` s.t. for each `i` in the returned indices,
  // `s[i..n) + s[0..i) = t`.
  private List<Integer> getIndices(int[] z, int n) {
    List<Integer> indices = new ArrayList<>();
    for (int i = n; i < n + n; ++i)
      if (z[i] >= n)
        indices.add(i - n);
    return indices;
  }
}

class Solution {
 public:
  // This dynamic programming table dp[k][i] represents the number of ways to
  // rearrange the string s after k steps such that it starts with s[i].
  // A string can be rotated from 1 to n - 1 times. The transition rule is
  // dp[k][i] = sum(dp[k - 1][j]) for all j != i. For example, when n = 4 and
  // k = 3, the table looks like this:
  //
  // -----------------------------------------------------------
  // |       | i = 0 | i = 1 | i = 2 | i = 3 | sum = (n - 1)^k |
  // -----------------------------------------------------------
  // | k = 0 |   1   |   0   |   0   |   0   |        1        |
  // | k = 1 |   0   |   1   |   1   |   1   |        3        |
  // | k = 2 |   3   |   2   |   2   |   2   |        9        |
  // | k = 3 |   6   |   7   |   7   |   7   |       27        |
  // -----------------------------------------------------------
  //
  // By observation, we have
  //   * dp[k][!0] = ((n - 1)^k - (-1)^k) / n
  //   * dp[k][0] = dp[k][!0] + (-1)^k
  int numberOfWays(string s, string t, long long k) {
    const int n = s.length();
    const int negOnePowK = (k % 2 == 0 ? 1 : -1);  // (-1)^k
    const vector<int> z = zFunction(s + t + t);
    const vector<int> indices = getIndices(z, n);
    vector<int> dp(2);  // dp[0] := dp[k][0]; dp[1] := dp[k][!0]
    dp[1] = (modPow(n - 1, k) - negOnePowK + kMod) % kMod *
            modPow(n, kMod - 2) % kMod;
    dp[0] = (dp[1] + negOnePowK + kMod) % kMod;
    return accumulate(indices.begin(), indices.end(), 0L,
                      [&](long acc, int index) {
      return (acc + dp[index == 0 ? 0 : 1]) % kMod;
    });
  }

 private:
  static constexpr int kMod = 1'000'000'007;

  long modPow(long x, long n) {
    if (n == 0)
      return 1;
    if (n % 2 == 1)
      return x * modPow(x, n - 1) % kMod;
    return modPow(x * x % kMod, n / 2);
  }

  // Returns the z array, where z[i] is the length of the longest prefix of
  // s[i..n) which is also a prefix of s.
  //
  // https://cp-algorithms.com/string/z-function.html#implementation
  vector<int> zFunction(const string& s) {
    const int n = s.length();
    vector<int> z(n);
    int l = 0;
    int r = 0;
    for (int i = 1; i < n; ++i) {
      if (i < r)
        z[i] = min(r - i, z[i - l]);
      while (i + z[i] < n && s[z[i]] == s[i + z[i]])
        ++z[i];
      if (i + z[i] > r) {
        l = i;
        r = i + z[i];
      }
    }
    return z;
  }

  // Returns the indices in `s` s.t. for each `i` in the returned indices,
  // `s[i..n) + s[0..i) = t`.
  vector<int> getIndices(const vector<int>& z, int n) {
    vector<int> indices;
    for (int i = n; i < n + n; ++i)
      if (z[i] >= n)
        indices.push_back(i - n);
    return indices;
  }
};


There is a directed graph consisting of `n` nodes numbered from `0` to `n - 1`
and `n` directed edges.

You are given a 0-indexed array `edges` where `edges[i]` indicates that there
is an edge from node `i` to node `edges[i]`.

Consider the following process on the graph:

* You start from a node `x` and keep visiting other nodes through edges until you reach a node that you have already visited before on this same process.

Return an array `answer` where `answer[i]` is the number of different nodes
that you will visit if you perform the process starting from node `i`.

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 countVisitedNodes(self, edges: List[int]) -> List[int]:
    n = len(edges)
    ans = [0] * n
    inDegrees = [0] * n
    seen = [False] * n
    stack = []

    for v in edges:
      inDegrees[v] += 1

    q = collections.deque([i for i, d in enumerate(inDegrees) if d == 0])

    while q:
      u = q.popleft()
      inDegrees[edges[u]] -= 1
      if inDegrees[edges[u]] == 0:
        q.append(edges[u])
      stack.append(u)
      seen[u] = True

    for i in range(n):
      if not seen[i]:
        self._fillCycle(edges, i, seen, ans)

    while stack:
      u = stack.pop()
      ans[u] = ans[edges[u]] + 1

    return ans

  def _fillCycle(self, edges: List[int], start: int, seen: List[bool], ans: List[int]) -> None:
    cycleLength = 0
    u = start
    while not seen[u]:
      cycleLength += 1
      seen[u] = True
      u = edges[u]
    ans[start] = cycleLength
    u = edges[start]
    while u != start:
      ans[u] = cycleLength
      u = edges[u]

class Solution {
  public int[] countVisitedNodes(List<Integer> edges) {
    final int n = edges.size();
    int[] ans = new int[n];
    int[] inDegrees = new int[n];
    boolean[] seen = new boolean[n];
    Queue<Integer> q = new ArrayDeque<>();
    Stack<Integer> stack = new Stack<>();

    for (int v : edges)
      ++inDegrees[v];

    // Perform topological sorting.
    for (int i = 0; i < n; ++i)
      if (inDegrees[i] == 0)
        q.add(i);

    // Push non-cyclic nodes to stack.
    while (!q.isEmpty()) {
      final int u = q.poll();
      if (--inDegrees[edges.get(u)] == 0)
        q.add(edges.get(u));
      stack.push(u);
      seen[u] = true;
    }

    // Fill the length of cyclic nodes.
    for (int i = 0; i < n; ++i)
      if (!seen[i])
        fillCycle(edges, i, seen, ans);

    // Fill the length of non-cyclic nodes.
    while (!stack.isEmpty()) {
      final int u = stack.pop();
      ans[u] = ans[edges.get(u)] + 1;
    }

    return ans;
  }

  private void fillCycle(List<Integer> edges, int start, boolean[] seen, int[] ans) {
    int cycleLength = 0;
    for (int u = start; !seen[u]; u = edges.get(u)) {
      ++cycleLength;
      seen[u] = true;
    }
    ans[start] = cycleLength;
    for (int u = edges.get(start); u != start; u = edges.get(u))
      ans[u] = cycleLength;
  }
}

class Solution {
 public:
  vector<int> countVisitedNodes(vector<int>& edges) {
    const int n = edges.size();
    vector<int> ans(n);
    vector<int> inDegrees(n);
    vector<bool> seen(n);
    queue<int> q;
    stack<int> stack;

    for (const int v : edges)
      ++inDegrees[v];

    // Perform topological sorting.
    for (int i = 0; i < n; ++i)
      if (inDegrees[i] == 0)
        q.push(i);

    // Push non-cyclic nodes to stack.
    while (!q.empty()) {
      const int u = q.front();
      q.pop();
      if (--inDegrees[edges[u]] == 0)
        q.push(edges[u]);
      stack.push(u);
      seen[u] = true;
    }

    // Fill the length of cyclic nodes.
    for (int i = 0; i < n; ++i)
      if (!seen[i])
        fillCycle(edges, i, seen, ans);

    // Fill the length of non-cyclic nodes.
    while (!stack.empty()) {
      const int u = stack.top();
      stack.pop();
      ans[u] = ans[edges[u]] + 1;
    }

    return ans;
  }

 private:
  void fillCycle(const vector<int>& edges, int start, vector<bool>& seen,
                 vector<int>& ans) {
    int cycleLength = 0;
    for (int u = start; !seen[u]; u = edges[u]) {
      ++cycleLength;
      seen[u] = true;
    }
    ans[start] = cycleLength;
    for (int u = edges[start]; u != start; u = edges[u])
      ans[u] = cycleLength;
  }
};


Longest Unequal Adjacent Groups Subsequence II

You are given a string array `words`, and an array `groups`, both arrays
having length `n`.

The hamming distance between two strings of equal length is the number of
positions at which the corresponding characters are different.

You need to select the longest subsequence from an array of indices `[0, 1,
..., n - 1]`, such that for the subsequence denoted as `[i0, i1, ..., ik-1]`
having length `k`, the following holds:

* For adjacent indices in the subsequence, their corresponding groups are unequal, i.e., `groups[ij] != groups[ij+1]`, for each `j` where `0 < j + 1 < k`.
* `words[ij]` and `words[ij+1]` are equal in length, and the hamming distance between them is `1`, where `0 < j + 1 < k`, for all indices in the subsequence.

Return a string array containing the words corresponding to the indices (in
order) in the selected subsequence. If there are multiple answers, return any
of them.

Note: strings in `words` may be unequal in length.

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 getWordsInLongestSubsequence(self, words: List[str], groups: List[int]) -> List[str]:
    ans = []
    n=len(words)
    dp = [1] * n
    prev = [-1] * n

    for i in range(1, n):
      for j in range(i):
        if groups[i] == groups[j]:
          continue
        if len(words[i]) != len(words[j]):
          continue
        if sum(a != b for a, b in zip(words[i], words[j])) != 1:
          continue
        if dp[i] < dp[j] + 1:
          dp[i] = dp[j] + 1
          prev[i] = j

    index = dp.index(max(dp))
    while index != -1:
      ans.append(words[index])
      index = prev[index]

    return ans[::-1]

class Solution {
  public List<String> getWordsInLongestSubsequence(int n, String[] words, int[] groups) {
    List<String> ans = new ArrayList<>();
    // dp[i] := the length of the longest subsequence ending in `words[i]`
    int[] dp = new int[n];
    Arrays.fill(dp, 1);
    // prev[i] := the best index of words[i]
    int[] prev = new int[n];
    Arrays.fill(prev, -1);

    for (int i = 1; i < n; ++i)
      for (int j = 0; j < i; ++j) {
        if (groups[i] == groups[j])
          continue;
        if (words[i].length() != words[j].length())
          continue;
        if (hammingDist(words[i], words[j]) != 1)
          continue;
        if (dp[i] < dp[j] + 1) {
          dp[i] = dp[j] + 1;
          prev[i] = j;
        }
      }

    // Find the last index of the subsequence.
    int index = getMaxIndex(dp);
    while (index != -1) {
      ans.add(words[index]);
      index = prev[index];
    }

    Collections.reverse(ans);
    return ans;
  }

  private int hammingDist(final String s1, final String s2) {
    int dist = 0;
    for (int i = 0; i < s1.length(); ++i)
      if (s1.charAt(i) != s2.charAt(i))
        ++dist;
    return dist;
  }

  private int getMaxIndex(int[] dp) {
    int maxIndex = 0;
    for (int i = 0; i < dp.length; ++i)
      if (dp[i] > dp[maxIndex])
        maxIndex = i;
    return maxIndex;
  }
}

class Solution {
 public:
  vector<string> getWordsInLongestSubsequence(int n, vector<string>& words,
                                              vector<int>& groups) {
    vector<string> ans;
    // dp[i] := the length of the longest subsequence ending in `words[i]`
    vector<int> dp(n, 1);
    // prev[i] := the best index of words[i]
    vector<int> prev(n, -1);

    for (int i = 1; i < n; ++i)
      for (int j = 0; j < i; ++j) {
        if (groups[i] == groups[j])
          continue;
        if (words[i].length() != words[j].length())
          continue;
        if (hammingDist(words[i], words[j]) != 1)
          continue;
        if (dp[i] < dp[j] + 1) {
          dp[i] = dp[j] + 1;
          prev[i] = j;
        }
      }

    // Find the last index of the subsequence.
    int index = ranges::max_element(dp) - dp.begin();
    while (index != -1) {
      ans.push_back(words[index]);
      index = prev[index];
    }

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

 private:
  int hammingDist(const string& s1, const string& s2) {
    int dist = 0;
    for (int i = 0; i < s1.length(); ++i)
      if (s1[i] != s2[i])
        ++dist;
    return dist;
  }
};


Shortest and Lexicographically Smallest Beautiful String

You are given a binary string `s` and a positive integer `k`.

A substring of `s` is beautiful if the number of `1`'s in it is exactly `k`.

Let `len` be the length of the shortest beautiful substring.

Return the lexicographically smallest beautiful substring of string `s` with
length equal to `len`. If `s` doesn't contain a beautiful substring, return an
empty string.

A string `a` is lexicographically larger than a string `b` (of the same
length) if in the first position where `a` and `b` differ, `a` has a character
strictly larger than the corresponding character in `b`.

* For example, `"abcd"` is lexicographically larger than `"abcc"` because the first position they differ is at the fourth character, and `d` is greater than `c`.

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 shortestBeautifulSubstring(self, s: str, k: int) -> str:
    bestLeft = -1
    minLength = len(s) + 1
    ones = 0

    l = 0
    for r, c in enumerate(s):
      if c == '1':
        ones += 1
      while ones == k:
        if r - l + 1 < minLength:
          bestLeft = l
          minLength = r - l + 1
        elif r - l + 1 == minLength and s[l:l + minLength] < s[bestLeft:bestLeft + minLength]:
          bestLeft = l
        if s[l] == '1':
          ones -= 1
        l += 1

    if bestLeft == -1:
      return ""
    else:
      return s[bestLeft:bestLeft + minLength]

class Solution {
  // Same as 76. Minimum Window Substring
  public String shortestBeautifulSubstring(String s, int k) {
    int bestLeft = -1;
    int minLength = s.length() + 1;
    int ones = 0;

    for (int l = 0, r = 0; r < s.length(); ++r) {
      if (s.charAt(r) == '1')
        ++ones;
      while (ones == k) {
        if (r - l + 1 < minLength) {
          bestLeft = l;
          minLength = r - l + 1;
        } else if (r - l + 1 == minLength &&
                   s.substring(l, l + minLength)
                           .compareTo(s.substring(bestLeft, bestLeft + minLength)) < 0) {
          bestLeft = l;
        }
        if (s.charAt(l++) == '1')
          --ones;
      }
    }

    return bestLeft == -1 ? "" : s.substring(bestLeft, bestLeft + minLength);
  }
}

class Solution {
 public:
  // Same as 76. Minimum Window Substring
  string shortestBeautifulSubstring(string s, int k) {
    int bestLeft = -1;
    int minLength = s.length() + 1;
    int ones = 0;

    for (int l = 0, r = 0; r < s.length(); ++r) {
      if (s[r] == '1')
        ++ones;
      while (ones == k) {
        if (r - l + 1 < minLength) {
          bestLeft = l;
          minLength = r - l + 1;
        } else if (r - l + 1 == minLength &&
                   s.compare(l, minLength, s, bestLeft, minLength) < 0) {
          bestLeft = l;
        }
        if (s[l++] == '1')
          --ones;
      }
    }

    return bestLeft == -1 ? "" : s.substr(bestLeft, minLength);
  }
};


Minimum Changes to Make K Semi-palindromes

Given a string `s` and an integer `k`, partition `s` into `k` substrings such
that the letter changes needed to make each substring a semi-palindrome are
minimized.

Return the minimum number of letter changes required.

A semi-palindrome is a special type of string that can be divided into
palindromes based on a repeating pattern. To check if a string is a semi-
palindrome:

1. Choose a positive divisor `d` of the string's length. `d` can range from `1` up to, but not including, the string's length. For a string of length `1`, it does not have a valid divisor as per this definition, since the only divisor is its length, which is not allowed.
2. For a given divisor `d`, divide the string into groups where each group contains characters from the string that follow a repeating pattern of length `d`. Specifically, the first group consists of characters at positions `1`, `1 + d`, `1 + 2d`, and so on; the second group includes characters at positions `2`, `2 + d`, `2 + 2d`, etc.
3. The string is considered a semi-palindrome if each of these groups forms a palindrome.

Consider the string `"abcabc"`:

* The length of `"abcabc"` is `6`. Valid divisors are `1`, `2`, and `3`.
* For `d = 1`: The entire string `"abcabc"` forms one group. Not a palindrome.
* For `d = 2`:
* Group 1 (positions `1, 3, 5`): `"acb"`
* Group 2 (positions `2, 4, 6`): `"bac"`
* Neither group forms a palindrome.
* For `d = 3`:
* Group 1 (positions `1, 4`): `"aa"`
* Group 2 (positions `2, 5`): `"bb"`
* Group 3 (positions `3, 6`): `"cc"`
* All groups form palindromes. Therefore, `"abcabc"` is a semi-palindrome.

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 minimumChanges(self, s: str, k: int) -> int:
    n = len(s)
    factors = self._getFactors(n)
    cost = self._getCost(s, n, factors)
    dp = [[n] * (k + 1) for _ in range(n + 1)]

    dp[n][0] = 0

    for i in range(n - 1, -1, -1):
      for j in range(1, k + 1):
        for l in range(i + 1, n):
          dp[i][j] = min(dp[i][j], dp[l + 1][j - 1] + cost[i][l])

    return dp[0][k]

  def _getFactors(self, n: int) -> List[List[int]]:
    factors = [[1] for _ in range(n + 1)]
    for d in range(2, n):
      for i in range(d * 2, n + 1, d):
        factors[i].append(d)
    return factors

  def _getCost(self, s: str, n: int, factors: List[List[int]]) -> List[List[int]]:
    cost = [[0] * n for _ in range(n)]
    for i in range(n):
      for j in range(i + 1, n):
        length = j - i + 1
        minCost = length
        for d in factors[length]:
          minCost = min(minCost, self._getCostD(s, i, j, d))
        cost[i][j] = minCost
    return cost

  def _getCostD(self, s: str, i: int, j: int, d: int) -> int:
    cost = 0
    for offset in range(d):
      l = i + offset
      r = j - d + 1 + offset
      while l < r:
        if s[l] != s[r]:
          cost += 1
        l += d
        r -= d
    return cost

class Solution {
  public int minimumChanges(String s, int k) {
    final int n = s.length();
    // factors[i] := factors of i
    List<Integer>[] factors = getFactors(n);
    // cost[i][j] := changes to make s[i..j] a semi-palindrome
    int[][] cost = getCost(s, n, factors);
    // dp[i][j] := the minimum changes to split s[i:] into j valid parts
    int[][] dp = new int[n + 1][k + 1];

    Arrays.stream(dp).forEach(A -> Arrays.fill(A, n));
    dp[n][0] = 0;

    for (int i = n - 1; i >= 0; --i)
      for (int j = 1; j <= k; ++j)
        for (int l = i + 1; l < n; ++l)
          dp[i][j] = Math.min(dp[i][j], dp[l + 1][j - 1] + cost[i][l]);

    return dp[0][k];
  }

  private List<Integer>[] getFactors(int n) {
    List<Integer>[] factors = new List[n + 1];
    for (int i = 1; i <= n; ++i)
      factors[i] = new ArrayList<>(List.of(1));
    for (int d = 2; d < n; ++d)
      for (int i = d * 2; i <= n; i += d)
        factors[i].add(d);
    return factors;
  }

  private int[][] getCost(final String s, int n, List<Integer>[] factors) {
    int[][] cost = new int[n][n];
    for (int i = 0; i < n; ++i)
      for (int j = i + 1; j < n; ++j) {
        final int length = j - i + 1;
        int minCost = length;
        for (final int d : factors[length])
          minCost = Math.min(minCost, getCost(s, i, j, d));
        cost[i][j] = minCost;
      }
    return cost;
  }

  // Returns the cost to make s[i..j] a semi-palindrome of `d`.
  private int getCost(final String s, int i, int j, int d) {
    int cost = 0;
    for (int offset = 0; offset < d; ++offset) {
      int l = i + offset;
      int r = j - d + 1 + offset;
      while (l < r) {
        if (s.charAt(l) != s.charAt(r))
          ++cost;
        l += d;
        r -= d;
      }
    }
    return cost;
  }
}

class Solution {
 public:
  int minimumChanges(string s, int k) {
    const int n = s.length();
    // factors[i] := factors of i
    const vector<vector<int>> factors = getFactors(n);
    // cost[i][j] := changes to make s[i..j] a semi-palindrome
    const vector<vector<int>> cost = getCost(s, n, factors);
    // dp[i][j] := the minimum changes to split s[i:] into j valid parts
    vector<vector<int>> dp(n + 1, vector<int>(k + 1, n));

    dp[n][0] = 0;

    for (int i = n - 1; i >= 0; --i)
      for (int j = 1; j <= k; ++j)
        for (int l = i + 1; l < n; ++l)
          dp[i][j] = min(dp[i][j], dp[l + 1][j - 1] + cost[i][l]);

    return dp[0][k];
  }

 private:
  vector<vector<int>> getFactors(int n) {
    vector<vector<int>> factors(n + 1);
    for (int i = 1; i <= n; ++i)
      factors[i].push_back(1);
    for (int d = 2; d < n; ++d)
      for (int i = d * 2; i <= n; i += d)
        factors[i].push_back(d);
    return factors;
  }

  vector<vector<int>> getCost(const string& s, int n,
                              const vector<vector<int>>& factors) {
    vector<vector<int>> cost(n, vector<int>(n));
    for (int i = 0; i < n; ++i)
      for (int j = i + 1; j < n; ++j) {
        const int length = j - i + 1;
        int minCost = length;
        for (const int d : factors[length])
          minCost = min(minCost, getCost(s, i, j, d));
        cost[i][j] = minCost;
      }
    return cost;
  }

  // Returns the cost to make s[i..j] a semi-palindrome of `d`.
  int getCost(const string& s, int i, int j, int d) {
    int cost = 0;
    for (int offset = 0; offset < d; ++offset) {
      int l = i + offset;
      int r = j - d + 1 + offset;
      while (l < r) {
        if (s[l] != s[r])
          ++cost;
        l += d;
        r -= d;
      }
    }
    return cost;
  }
};


You are given a 0-indexed integer array `nums`. A pair of integers `x` and `y`
is called a strong pair if it satisfies the condition:

* `|x - y| <= min(x, y)`

You need to select two integers from `nums` such that they form a strong pair
and their bitwise `XOR` is the maximum among all strong pairs in the array.

Return the maximum `XOR` value out of all possible strong pairs in the array
`nums`.

Note that you can pick the same integer twice to form a pair.

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

class TrieNode:
  def __init__(self):
    self.children: List[Optional[TrieNode]] = [None] * 2
    self.min = math.inf
    self.max = -math.inf

class BitTrie:
  def __init__(self, maxBit: int):
    self.maxBit = maxBit
    self.root = TrieNode()

  def insert(self, num: int) -> None:
    node = self.root
    for i in range(self.maxBit, -1, -1):
      bit = num >> i & 1
      if not node.children[bit]:
        node.children[bit] = TrieNode()
      node = node.children[bit]
      node.min = min(node.min, num)
      node.max = max(node.max, num)

  def getMaxXor(self, x: int) -> int:
    maxXor = 0
    node = self.root
    for i in range(self.maxBit, -1, -1):
      bit = x >> i & 1
      toggleBit = bit ^ 1
      if node.children[toggleBit] and node.children[toggleBit].max > x and node.children[toggleBit].min <= 2 * x:
        maxXor = maxXor | 1 << i
        node = node.children[toggleBit]
      elif node.children[bit]:
        node = node.children[bit]
      else:
        return 0
    return maxXor

class Solution:
  def maximumStrongPairXor(self, nums: List[int]) -> int:
    maxNum = max(nums)
    maxBit = int(math.log2(maxNum))
    bitTrie = BitTrie(maxBit)

    for num in nums:
      bitTrie.insert(num)

    return max(bitTrie.getMaxXor(num) for num in nums)

class TrieNode {
  public TrieNode[] children = new TrieNode[2];
  public int mn = Integer.MAX_VALUE;
  public int mx = Integer.MIN_VALUE;
}

class BitTrie {
  public BitTrie(int maxBit) {
    this.maxBit = maxBit;
  }

  public void insert(int num) {
    TrieNode node = root;
    for (int i = maxBit; i >= 0; --i) {
      final int bit = (int) (num >> i & 1);
      if (node.children[bit] == null)
        node.children[bit] = new TrieNode();
      node = node.children[bit];
      node.mn = Math.min(node.mn, num);
      node.mx = Math.max(node.mx, num);
    }
  }

  // Returns max(x ^ y), where |x - y| <= min(x, y).
  //
  // If x <= y, |x - y| <= min(x, y) can be written as y - x <= x.
  // So, y <= 2 * x.
  public int getMaxXor(int x) {
    int maxXor = 0;
    TrieNode node = root;
    for (int i = maxBit; i >= 0; --i) {
      final int bit = (int) (x >> i & 1);
      final int toggleBit = bit ^ 1;
      // If `node.children[toggleBit].mx > x`, it means there's a number in the
      // node that satisfies the condition to ensure that x <= y among x and y.
      // If `node.children[toggleBit].mn <= 2 * x`, it means there's a number
      // in the node that satisfies the condition for a valid y.
      if (node.children[toggleBit] != null && node.children[toggleBit].mx > x &&
          node.children[toggleBit].mn <= 2 * x) {
        maxXor = maxXor | 1 << i;
        node = node.children[toggleBit];
      } else if (node.children[bit] != null) {
        node = node.children[bit];
      } else { // There's nothing in the Bit Trie.
        return 0;
      }
    }
    return maxXor;
  }

  private int maxBit;
  private TrieNode root = new TrieNode();
}

class Solution {
  // Similar to 421. Maximum XOR of Two Numbers in an Array
  public int maximumStrongPairXor(int[] nums) {
    final int maxNum = Arrays.stream(nums).max().getAsInt();
    final int maxBit = (int) (Math.log(maxNum) / Math.log(2));
    int ans = 0;
    BitTrie bitTrie = new BitTrie(maxBit);

    for (final int num : nums)
      bitTrie.insert(num);

    for (final int num : nums)
      ans = Math.max(ans, bitTrie.getMaxXor(num));

    return ans;
  }
}

struct TrieNode {
  vector<shared_ptr<TrieNode>> children;
  TrieNode() : children(2) {}
  int mn = INT_MAX;
  int mx = INT_MIN;
};

class BitTrie {
 public:
  BitTrie(int maxBit) : maxBit(maxBit) {}

  void insert(int num) {
    shared_ptr<TrieNode> node = root;
    for (int i = maxBit; i >= 0; --i) {
      const int bit = num >> i & 1;
      if (node->children[bit] == nullptr)
        node->children[bit] = make_shared<TrieNode>();
      node = node->children[bit];
      node->mn = min(node->mn, num);
      node->mx = max(node->mx, num);
    }
  }

  // Returns max(x ^ y), where |x - y| <= min(x, y).
  //
  // If x <= y, |x - y| <= min(x, y) can be written as y - x <= x.
  // So, y <= 2 * x.
  int getMaxXor(int x) {
    int maxXor = 0;
    shared_ptr<TrieNode> node = root;
    for (int i = maxBit; i >= 0; --i) {
      const int bit = x >> i & 1;
      const int toggleBit = bit ^ 1;
      // If `node.children[toggleBit].mx > x`, it means there's a number in the
      // node that satisfies the condition to ensure that x <= y among x and y.
      // If `node.children[toggleBit].mn <= 2 * x`, it means there's a number
      // in the node that satisfies the condition for a valid y.
      if (node->children[toggleBit] != nullptr &&
          node->children[toggleBit]->mx > x &&
          node->children[toggleBit]->mn <= 2 * x) {
        maxXor = maxXor | 1 << i;
        node = node->children[toggleBit];
      } else if (node->children[bit] != nullptr) {
        node = node->children[bit];
      } else {  // There's nothing in the Bit Trie.
        return 0;
      }
    }
    return maxXor;
  }

 private:
  const int maxBit;
  shared_ptr<TrieNode> root = make_shared<TrieNode>();
};

class Solution {
 public:
  // Similar to 421. Maximum XOR of Two Numbers in an Array
  int maximumStrongPairXor(vector<int>& nums) {
    const int maxNum = ranges::max(nums);
    const int maxBit = static_cast<int>(log2(maxNum));
    int ans = 0;
    BitTrie bitTrie(maxBit);

    for (const int num : nums)
      bitTrie.insert(num);

    for (const int num : nums)
      ans = max(ans, bitTrie.getMaxXor(num));

    return ans;
  }
};


Find Building Where Alice and Bob Can Meet

You are given a 0-indexed array `heights` of positive integers, where
`heights[i]` represents the height of the `ith` building.

If a person is in building `i`, they can move to any other building `j` if and
only if `i < j` and `heights[i] < heights[j]`.

You are also given another array `queries` where `queries[i] = [ai, bi]`. On
the `ith` query, Alice is in building `ai` while Bob is in building `bi`.

Return an array `ans` where `ans[i]` is the index of the leftmost building
where Alice and Bob can meet on the `ith` query. If Alice and Bob cannot move
to a common building on query `i`, set `ans[i]` to `-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 IndexedQuery:
  def __init__(self, queryIndex: int, a: int, b: int):
    self.queryIndex = queryIndex
    self.a = a
    self.b = b

  def __iter__(self):
    yield self.queryIndex
    yield self.a
    yield self.b

class Solution:
  def leftmostBuildingQueries(self, heights: List[int], queries: List[List[int]]) -> List[int]:
    ans = [-1] * len(queries)
    stack = []

    heightsIndex = len(heights) - 1
    for queryIndex, a, b in sorted([IndexedQuery(i, min(a, b), max(a, b)) for i, (a, b) in enumerate(queries)], key=lambda iq: -iq.b):
      if a == b or heights[a] < heights[b]:
        ans[queryIndex] = b
      else:
        while heightsIndex > b:
          while stack and heights[stack[-1]] <= heights[heightsIndex]:
            stack.pop()
          stack.append(heightsIndex)
          heightsIndex -= 1
        j = self._lastGreater(stack, a, heights)
        if j != -1:
          ans[queryIndex] = stack[j]

    return ans

  def _lastGreater(self, A: List[int], target: int, heights: List[int]):
    l = -1
    r = len(A) - 1
    while l < r:
      m = (l + r + 1) // 2
      if heights[A[m]] > heights[target]:
        l = m
      else:
        r = m - 1
    return l

class Solution {
  // Similar to 2736. Maximum Sum Queries
  public int[] leftmostBuildingQueries(int[] heights, int[][] queries) {
    IndexedQuery[] indexedQueries = getIndexedQueries(queries);
    int[] ans = new int[queries.length];
    Arrays.fill(ans, -1);
    // Store indices (heightsIndex) of heights with heights[heightsIndex] in
    // descending order.
    List<Integer> stack = new ArrayList<>();

    // Iterate through queries and heights simultaneously.
    int heightsIndex = heights.length - 1;
    for (IndexedQuery indexedQuery : indexedQueries) {
      final int queryIndex = indexedQuery.queryIndex;
      final int a = indexedQuery.a;
      final int b = indexedQuery.b;
      if (a == b || heights[a] < heights[b]) {
        // 1. Alice and Bob are already in the same index (a == b) or
        // 2. Alice can jump from a -> b (heights[a] < heights[b]).
        ans[queryIndex] = b;
      } else {
        // Now, a < b and heights[a] >= heights[b].
        // Gradually add heights with an index > b to the monotonic stack.
        while (heightsIndex > b) {
          // heights[heightsIndex] is a better candidate, given that
          // heightsIndex is smaller than the indices in the stack and
          // heights[heightsIndex] is larger or equal to the heights mapped in
          // the stack.
          while (!stack.isEmpty() && heights[stack.get(stack.size() - 1)] <= heights[heightsIndex])
            stack.remove(stack.size() - 1);
          stack.add(heightsIndex--);
        }
        // Binary search to find the smallest index j such that j > b and
        // heights[j] > heights[a], thereby ensuring heights[t] > heights[b].
        final int j = lastGreater(stack, a, heights);
        if (j != -1)
          ans[queryIndex] = stack.get(j);
      }
    }

    return ans;
  }

  private record IndexedQuery(int queryIndex, int a, int b){};

  // Returns the last index i in A s.t. heights[A.get(i)] is > heights[target].
  private int lastGreater(List<Integer> A, int target, int[] heights) {
    int l = -1;
    int r = A.size() - 1;
    while (l < r) {
      final int m = (l + r + 1) / 2;
      if (heights[A.get(m)] > heights[target])
        l = m;
      else
        r = m - 1;
    }
    return l;
  }

  private IndexedQuery[] getIndexedQueries(int[][] queries) {
    IndexedQuery[] indexedQueries = new IndexedQuery[queries.length];
    for (int i = 0; i < queries.length; ++i) {
      // Make sure that a <= b.
      final int a = Math.min(queries[i][0], queries[i][1]);
      final int b = Math.max(queries[i][0], queries[i][1]);
      indexedQueries[i] = new IndexedQuery(i, a, b);
    }
    Arrays.sort(indexedQueries, Comparator.comparing(IndexedQuery::b, Comparator.reverseOrder()));
    return indexedQueries;
  }
}

struct IndexedQuery {
  int queryIndex;
  int a;  // Alice's index
  int b;  // Bob's index
};

class Solution {
 public:
  // Similar to 2736. Maximum Sum Queries
  vector<int> leftmostBuildingQueries(vector<int>& heights,
                                      vector<vector<int>>& queries) {
    vector<int> ans(queries.size(), -1);
    // Store indices (heightsIndex) of heights with heights[heightsIndex] in
    // descending order.
    vector<int> stack;

    // Iterate through queries and heights simultaneously.
    int heightsIndex = heights.size() - 1;
    for (const auto& [queryIndex, a, b] : getIndexedQueries(queries)) {
      if (a == b || heights[a] < heights[b]) {
        // 1. Alice and Bob are already in the same index (a == b) or
        // 2. Alice can jump from a -> b (heights[a] < heights[b]).
        ans[queryIndex] = b;
      } else {
        // Now, a < b and heights[a] >= heights[b].
        // Gradually add heights with an index > b to the monotonic stack.
        while (heightsIndex > b) {
          // heights[heightsIndex] is a better candidate, given that
          // heightsIndex is smaller than the indices in the stack and
          // heights[heightsIndex] is larger or equal to the heights mapped in
          // the stack.
          while (!stack.empty() &&
                 heights[stack.back()] <= heights[heightsIndex])
            stack.pop_back();
          stack.push_back(heightsIndex--);
        }
        // Binary search to find the smallest index j such that j > b and
        // heights[j] > heights[a], thereby ensuring heights[j] > heights[b].
        if (const auto it = upper_bound(
                stack.rbegin(), stack.rend(), a,
                [&](int a, int b) { return heights[a] < heights[b]; });
            it != stack.rend())
          ans[queryIndex] = *it;
      }
    }

    return ans;
  }

 private:
  vector<IndexedQuery> getIndexedQueries(const vector<vector<int>>& queries) {
    vector<IndexedQuery> indexedQueries;
    for (int i = 0; i < queries.size(); ++i) {
      // Make sure that a <= b.
      const int a = min(queries[i][0], queries[i][1]);
      const int b = max(queries[i][0], queries[i][1]);
      indexedQueries.push_back({i, a, b});
    }
    ranges::sort(
        indexedQueries,
        [](const IndexedQuery& a, const IndexedQuery& b) { return a.b > b.b; });
    return indexedQueries;
  }
};


Make Lexicographically Smallest Array by Swapping Elements

You are given a 0-indexed array of positive integers `nums` and a positive
integer `limit`.

In one operation, you can choose any two indices `i` and `j` and swap
`nums[i]` and `nums[j]` if `|nums[i] - nums[j]| <= limit`.

Return the lexicographically smallest array that can be obtained by performing
the operation any number of times.

An array `a` is lexicographically smaller than an array `b` if in the first
position where `a` and `b` differ, array `a` has an element that is less than
the corresponding element in `b`. For example, the array `[2,10,3]` is
lexicographically smaller than the array `[10,2,3]` because they differ at
index `0` and `2 < 10`.

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 lexicographicallySmallestArray(self, nums: List[int], limit: int) -> List[int]:
    ans = [0] * len(nums)
    numAndIndexes = sorted([(num, i) for i, num in enumerate(nums)])
    numAndIndexesGroups: List[List[Tuple[int, int]]] = []

    for numAndIndex in numAndIndexes:
      if not numAndIndexesGroups or numAndIndex[0] - numAndIndexesGroups[-1][-1][0] > limit:
        numAndIndexesGroups.append([numAndIndex])
      else:
        numAndIndexesGroups[-1].append(numAndIndex)

    for numAndIndexesGroup in numAndIndexesGroups:
      sortedNums = [num for num, _ in numAndIndexesGroup]
      sortedIndices = sorted([index for _, index in numAndIndexesGroup])
      for num, index in zip(sortedNums, sortedIndices):
        ans[index] = num

    return ans

class Solution {
  public int[] lexicographicallySmallestArray(int[] nums, int limit) {
    int[] ans = new int[nums.length];
    List<List<Pair<Integer, Integer>>> numAndIndexesGroups = new ArrayList<>();

    for (Pair<Integer, Integer> numAndIndex : getNumAndIndexes(nums))
      if (numAndIndexesGroups.isEmpty() ||
          numAndIndex.getKey() -
                  numAndIndexesGroups.get(numAndIndexesGroups.size() - 1)
                      .get(numAndIndexesGroups.get(numAndIndexesGroups.size() - 1).size() - 1)
                      .getKey() >
              limit) {
        // Start a new group.
        numAndIndexesGroups.add(new ArrayList<>(List.of(numAndIndex)));
      } else {
        // Append to the existing group.
        numAndIndexesGroups.get(numAndIndexesGroups.size() - 1).add(numAndIndex);
      }

    for (List<Pair<Integer, Integer>> numAndIndexesGroup : numAndIndexesGroups) {
      List<Integer> sortedNums = new ArrayList<>();
      List<Integer> sortedIndices = new ArrayList<>();
      for (Pair<Integer, Integer> pair : numAndIndexesGroup) {
        sortedNums.add(pair.getKey());
        sortedIndices.add(pair.getValue());
      }
      sortedIndices.sort(null);
      for (int i = 0; i < sortedNums.size(); ++i) {
        ans[sortedIndices.get(i)] = sortedNums.get(i);
      }
    }

    return ans;
  }

  private Pair<Integer, Integer>[] getNumAndIndexes(int[] nums) {
    Pair<Integer, Integer>[] numAndIndexes = new Pair[nums.length];
    for (int i = 0; i < nums.length; ++i)
      numAndIndexes[i] = new Pair<>(nums[i], i);
    Arrays.sort(numAndIndexes, Comparator.comparingInt(Pair::getKey));
    return numAndIndexes;
  }
}

class Solution {
 public:
  vector<int> lexicographicallySmallestArray(vector<int>& nums, int limit) {
    vector<int> ans(nums.size());
    // [[(num, index)]], where the difference between in each pair in each
    // `[(num, index)]` group <= `limit`
    vector<vector<pair<int, int>>> numAndIndexesGroups;

    for (const pair<int, int>& numAndIndex : getNumAndIndexes(nums))
      if (numAndIndexesGroups.empty() ||
          numAndIndex.first - numAndIndexesGroups.back().back().first > limit) {
        // Start a new group.
        numAndIndexesGroups.push_back({numAndIndex});
      } else {
        // Append to the existing group.
        numAndIndexesGroups.back().push_back(numAndIndex);
      }

    for (const vector<pair<int, int>>& numAndIndexesGroup :
         numAndIndexesGroups) {
      vector<int> sortedNums;
      vector<int> sortedIndices;
      for (const auto& [num, index] : numAndIndexesGroup) {
        sortedNums.push_back(num);
        sortedIndices.push_back(index);
      }
      ranges::sort(sortedIndices);
      for (int i = 0; i < sortedNums.size(); ++i)
        ans[sortedIndices[i]] = sortedNums[i];
    }

    return ans;
  }

 private:
  vector<pair<int, int>> getNumAndIndexes(const vector<int>& nums) {
    vector<pair<int, int>> numAndIndexes;
    for (int i = 0; i < nums.size(); ++i)
      numAndIndexes.emplace_back(nums[i], i);
    ranges::sort(numAndIndexes);
    return numAndIndexes;
  }
};


You are given a string `word` and an integer `k`.

A substring `s` of `word` is complete if:

* Each character in `s` occurs exactly `k` times.
* The difference between two adjacent characters is at most `2`. That is, for any two adjacent characters `c1` and `c2` in `s`, the absolute difference in their positions in the alphabet is at most `2`.

Return the number of complete substrings of `word`.

A substring is a non-empty contiguous sequence of characters in a 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 countCompleteSubstrings(self, word: str, k: int) -> int:
    uniqueLetters = len(set(word))
    return sum(self._countCompleteStrings(word, k, windowSize) for windowSize in range(k, k * uniqueLetters + 1, k))

  def _countCompleteStrings(self, word: str, k: int, windowSize: int) -> int:
    res = 0
    countLetters = 0
    count = collections.Counter()

    for i, c in enumerate(word):
      count[c] += 1
      countLetters += 1
      if i > 0 and abs(ord(c) - ord(word[i - 1])) > 2:
        count = collections.Counter()
        count[c] += 1
        countLetters = 1
      if countLetters == windowSize + 1:
        count[word[i - windowSize]] -= 1
        countLetters -= 1
      if countLetters == windowSize:
        res += all(freq == 0 or freq == k for freq in count.values())

    return res

class Solution {
  public int countCompleteSubstrings(String word, int k) {
    final int uniqueLetters = word.chars().boxed().collect(Collectors.toSet()).size();
    int ans = 0;

    for (int windowSize = k; windowSize <= k * uniqueLetters && windowSize <= word.length();
         windowSize += k) {
      ans += countCompleteStrings(word, k, windowSize);
    }

    return ans;
  }

  // Returns the number of complete substrings of `windowSize` of `word`.
  private int countCompleteStrings(final String word, int k, int windowSize) {
    int res = 0;
    int countLetters = 0; // the number of letters in the running substring
    int[] count = new int[26];

    for (int i = 0; i < word.length(); ++i) {
      ++count[word.charAt(i) - 'a'];
      ++countLetters;
      if (i > 0 && Math.abs(word.charAt(i) - word.charAt(i - 1)) > 2) {
        count = new int[26];
        // Start a new substring starting at word[i].
        ++count[word.charAt(i) - 'a'];
        countLetters = 1;
      }
      if (countLetters == windowSize + 1) {
        --count[word.charAt(i - windowSize) - 'a'];
        --countLetters;
      }
      if (countLetters == windowSize)
        res += Arrays.stream(count).allMatch(freq -> freq == 0 || freq == k) ? 1 : 0;
    }

    return res;
  }
}

class Solution {
 public:
  int countCompleteSubstrings(string word, int k) {
    const int uniqueLetters =
        unordered_set<char>{word.begin(), word.end()}.size();
    int ans = 0;

    for (int windowSize = k;
         windowSize <= k * uniqueLetters && windowSize <= word.length();
         windowSize += k)
      ans += countCompleteStrings(word, k, windowSize);

    return ans;
  }

 private:
  // Returns the number of complete substrings of `windowSize` of `word`.
  int countCompleteStrings(const string& word, int k, int windowSize) {
    int res = 0;
    int countLetters = 0;  // the number of letters in the running substring
    vector<int> count(26);

    for (int i = 0; i < word.length(); ++i) {
      ++count[word[i] - 'a'];
      ++countLetters;
      if (i > 0 && abs(word[i] - word[i - 1]) > 2) {
        count = vector<int>(26);
        // Start a new substring starting at word[i].
        ++count[word[i] - 'a'];
        countLetters = 1;
      }
      if (countLetters == windowSize + 1) {
        --count[word[i - windowSize] - 'a'];
        --countLetters;
      }
      if (countLetters == windowSize)
        res += ranges::all_of(count,
                              [k](int freq) { return freq == 0 || freq == k; })
                   ? 1
                   : 0;
    }

    return res;
  }
};


Number of Possible Sets of Closing Branches

There is a company with `n` branches across the country, some of which are
connected by roads. Initially, all branches are reachable from each other by
traveling some roads.

The company has realized that they are spending an excessive amount of time
traveling between their branches. As a result, they have decided to close down
some of these branches (possibly none). However, they want to ensure that the
remaining branches have a distance of at most `maxDistance` from each other.

The distance between two branches is the minimum total traveled length needed
to reach one branch from another.

You are given integers `n`, `maxDistance`, and a 0-indexed 2D array `roads`,
where `roads[i] = [ui, vi, wi]` represents the undirected road between
branches `ui` and `vi` with length `wi`.

Return the number of possible sets of closing branches, so that any branch has
a distance of at most `maxDistance` from any other.

Note that, after closing a branch, the company will no longer have access to
any roads connected to it.

Note that, multiple roads are allowed.

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 numberOfSets(self, n: int, maxDistance: int, roads: List[List[int]]) -> int:
    return sum(self._floydWarshall(n, maxDistance, roads, mask) <= maxDistance for mask in range(1 << n))

  def _floydWarshall(self, n: int, maxDistanceThreshold: int, roads: List[List[int]], mask: int) -> List[List[int]]:
    maxDistance = 0
    dist = [[maxDistanceThreshold + 1] * n for _ in range(n)]

    for i in range(n):
      if mask >> i & 1:
        dist[i][i] = 0

    for u, v, w in roads:
      if mask >> u & 1 and mask >> v & 1:
        dist[u][v] = min(dist[u][v], w)
        dist[v][u] = min(dist[v][u], w)

    for k in range(n):
      if mask >> k & 1:
        for i in range(n):
          if mask >> i & 1:
            for j in range(n):
              if mask >> j & 1:
                dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])

    for i in range(n):
      if mask >> i & 1:
        for j in range(i + 1, n):
          if mask >> j & 1:
            maxDistance = max(maxDistance, dist[i][j])

    return maxDistance

class Solution {
  public int numberOfSets(int n, int maxDistance, int[][] roads) {
    final int maxMask = 1 << n;
    int ans = 0;

    for (int mask = 0; mask < maxMask; ++mask)
      if (floydWarshall(n, maxDistance, roads, mask) <= maxDistance)
        ++ans;

    return ans;
  }

  private int floydWarshall(int n, int maxDistanceThreshold, int[][] roads, int mask) {
    int maxDistance = 0;
    int[][] dist = new int[n][n];
    Arrays.stream(dist).forEach(A -> Arrays.fill(A, maxDistanceThreshold + 1));

    for (int i = 0; i < n; ++i)
      if ((mask >> i & 1) == 1)
        dist[i][i] = 0;

    for (int[] road : roads) {
      final int u = road[0];
      final int v = road[1];
      final int w = road[2];
      if ((mask >> u & 1) == 1 && (mask >> v & 1) == 1) {
        dist[u][v] = Math.min(dist[u][v], w);
        dist[v][u] = Math.min(dist[v][u], w);
      }
    }

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

    for (int i = 0; i < n; ++i)
      if ((mask >> i & 1) == 1)
        for (int j = i + 1; j < n; ++j)
          if ((mask >> j & 1) == 1)
            maxDistance = Math.max(maxDistance, dist[i][j]);

    return maxDistance;
  }
}

class Solution {
 public:
  int numberOfSets(int n, int maxDistance, vector<vector<int>>& roads) {
    const int maxMask = 1 << n;
    int ans = 0;

    for (int mask = 0; mask < maxMask; ++mask)
      if (floydWarshall(n, maxDistance, roads, mask) <= maxDistance)
        ++ans;

    return ans;
  }

 private:
  // Returns the maximum distance between any two branches, where the mask
  // represents the selected branches.
  int floydWarshall(int n, int maxDistanceThreshold, vector<vector<int>>& roads,
                    int mask) {
    int maxDistance = 0;
    vector<vector<int>> dist(n, vector<int>(n, maxDistanceThreshold + 1));

    for (int i = 0; i < n; ++i)
      if (mask >> i & 1)
        dist[i][i] = 0;

    for (const vector<int>& road : roads) {
      const int u = road[0];
      const int v = road[1];
      const int w = road[2];
      if (mask >> u & 1 && mask >> v & 1) {
        dist[u][v] = min(dist[u][v], w);
        dist[v][u] = min(dist[v][u], w);
      }
    }

    for (int k = 0; k < n; ++k)
      if (mask >> k & 1)
        for (int i = 0; i < n; ++i)
          if (mask >> i & 1)
            for (int j = 0; j < n; ++j)
              if (mask >> j & 1)
                dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);

    for (int i = 0; i < n; ++i)
      if (mask >> i & 1)
        for (int j = i + 1; j < n; ++j)
          if (mask >> j & 1)
            maxDistance = max(maxDistance, dist[i][j]);

    return maxDistance;
  }
};


Find Number of Coins to Place in Tree Nodes

You are given an undirected tree with `n` nodes labeled from `0` to `n - 1`,
and rooted at node `0`. You are given a 2D integer array `edges` of length `n
- 1`, where `edges[i] = [ai, bi]` indicates that there is an edge between
nodes `ai` and `bi` in the tree.

You are also given a 0-indexed integer array `cost` of length `n`, where
`cost[i]` is the cost assigned to the `ith` node.

You need to place some coins on every node of the tree. The number of coins to
be placed at node `i` can be calculated as:

* If size of the subtree of node `i` is less than `3`, place `1` coin.
* Otherwise, place an amount of coins equal to the maximum product of cost values assigned to `3` distinct nodes in the subtree of node `i`. If this product is negative, place `0` coins.

Return an array `coin` of size `n` such that `coin[i]` is the number of coins
placed at node `i`.

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

class ChildCost:
  def __init__(self, cost: int):
    self.numNodes = 1
    self.maxPosCosts = [cost] if cost > 0 else []
    self.minNegCosts = [cost] if cost < 0 else []

  def update(self, childCost: 'ChildCost') -> None:
    self.numNodes += childCost.numNodes
    self.maxPosCosts.extend(childCost.maxPosCosts)
    self.minNegCosts.extend(childCost.minNegCosts)
    self.maxPosCosts.sort(reverse=True)
    self.minNegCosts.sort()
    self.maxPosCosts = self.maxPosCosts[:3]
    self.minNegCosts = self.minNegCosts[:2]

  def maxProduct(self) -> int:
    if self.numNodes < 3:
      return 1
    if not self.maxPosCosts:
      return 0
    res = 0
    if len(self.maxPosCosts) == 3:
      res = self.maxPosCosts[0] * self.maxPosCosts[1] * self.maxPosCosts[2]
    if len(self.minNegCosts) == 2:
      res = max(res, self.minNegCosts[0] * self.minNegCosts[1] * self.maxPosCosts[0])
    return res

class Solution:
  def placedCoins(self, edges: List[List[int]], cost: List[int]) -> List[int]:
    n = len(cost)
    ans = [0] * n
    tree = [[] for _ in range(n)]

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

    def dfs(u: int, prev: int) -> None:
      res = ChildCost(cost[u])
      for v in tree[u]:
        if v != prev:
          res.update(dfs(v, u))
      ans[u] = res.maxProduct()
      return res

    dfs(0, -1)
    return ans

class ChildCost {
  public ChildCost(int cost) {
    if (cost > 0)
      maxPosCosts.add(cost);
    else
      minNegCosts.add(cost);
  }

  public void update(ChildCost childCost) {
    numNodes += childCost.numNodes;
    maxPosCosts.addAll(childCost.maxPosCosts);
    minNegCosts.addAll(childCost.minNegCosts);
    maxPosCosts.sort(Comparator.reverseOrder());
    minNegCosts.sort(Comparator.naturalOrder());
    if (maxPosCosts.size() > 3)
      maxPosCosts = maxPosCosts.subList(0, 3);
    if (minNegCosts.size() > 2)
      minNegCosts = minNegCosts.subList(0, 2);
  }

  public long maxProduct() {
    if (numNodes < 3)
      return 1;
    if (maxPosCosts.isEmpty())
      return 0;
    long res = 0;
    if (maxPosCosts.size() == 3)
      res = (long) maxPosCosts.get(0) * maxPosCosts.get(1) * maxPosCosts.get(2);
    if (minNegCosts.size() == 2)
      res = Math.max(res, (long) minNegCosts.get(0) * minNegCosts.get(1) * maxPosCosts.get(0));
    return res;
  }

  private int numNodes = 1;
  private List<Integer> maxPosCosts = new ArrayList<>();
  private List<Integer> minNegCosts = new ArrayList<>();
}

class Solution {
  public long[] placedCoins(int[][] edges, int[] cost) {
    final int n = cost.length;
    long[] ans = new long[n];
    List<Integer>[] tree = new List[n];

    for (int i = 0; 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);
    }

    dfs(tree, 0, /*prev=*/-1, cost, ans);
    return ans;
  }

  private ChildCost dfs(List<Integer>[] tree, int u, int prev, int[] cost, long[] ans) {
    ChildCost res = new ChildCost(cost[u]);
    for (final int v : tree[u])
      if (v != prev)
        res.update(dfs(tree, v, u, cost, ans));
    ans[u] = res.maxProduct();
    return res;
  }
}

class ChildCost {
 public:
  ChildCost(int cost) {
    numNodes = 1;
    if (cost > 0)
      maxPosCosts.push_back(cost);
    else
      minNegCosts.push_back(cost);
  }

  void update(ChildCost childCost) {
    numNodes += childCost.numNodes;
    ranges::copy(childCost.maxPosCosts, back_inserter(maxPosCosts));
    ranges::copy(childCost.minNegCosts, back_inserter(minNegCosts));
    ranges::sort(maxPosCosts, greater<int>());
    ranges::sort(minNegCosts);
    maxPosCosts.resize(min(static_cast<int>(maxPosCosts.size()), 3));
    minNegCosts.resize(min(static_cast<int>(minNegCosts.size()), 2));
  }

  long maxProduct() {
    if (numNodes < 3)
      return 1;
    if (maxPosCosts.empty())
      return 0;
    long res = 0;
    if (maxPosCosts.size() == 3)
      res = static_cast<long>(maxPosCosts[0]) * maxPosCosts[1] * maxPosCosts[2];
    if (minNegCosts.size() == 2)
      res = max(res, static_cast<long>(minNegCosts[0]) * minNegCosts[1] *
                         maxPosCosts[0]);
    return res;
  }

 private:
  int numNodes;
  vector<int> maxPosCosts;
  vector<int> minNegCosts;
};

class Solution {
 public:
  vector<long long> placedCoins(vector<vector<int>>& edges, vector<int>& cost) {
    const int n = cost.size();
    vector<long long> ans(n);
    vector<vector<int>> tree(n);

    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);
    }

    dfs(tree, 0, /*prev=*/-1, cost, ans);
    return ans;
  }

 private:
  ChildCost dfs(const vector<vector<int>>& tree, int u, int prev,
                const vector<int>& cost, vector<long long>& ans) {
    ChildCost res(cost[u]);
    for (const int v : tree[u])
      if (v != prev)
        res.update(dfs(tree, v, u, cost, ans));
    ans[u] = res.maxProduct();
    return res;
  }
};


You are given two 0-indexed strings `source` and `target`, both of length `n`
and consisting of lowercase English letters. You are also given two 0-indexed
character arrays `original` and `changed`, and an integer array `cost`, where
`cost[i]` represents the cost of changing the character `original[i]` to the
character `changed[i]`.

You start with the string `source`. In one operation, you can pick a character
`x` from the string and change it to the character `y` at a cost of `z` if
there exists any index `j` such that `cost[j] == z`, `original[j] == x`, and
`changed[j] == y`.

Return the minimum cost to convert the string `source` to the string `target`
using any number of operations. If it is impossible to convert `source` to
`target`, return `-1`.

Note that there may exist indices `i`, `j` such that `original[j] ==
original[i]` and `changed[j] == changed[i]`.

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 minimumCost(self, source: str, target: str, original: List[str], changed: List[str], cost: List[int]) -> int:
    ans = 0
    dist = [[math.inf] * 26 for _ in range(26)]

    for a, b, c in zip(original, changed, cost):
      u = ord(a) - ord('a')
      v = ord(b) - ord('a')
      dist[u][v] = min(dist[u][v], c)

    for k in range(26):
      for i in range(26):
        if dist[i][k] < math.inf:
          for j in range(26):
            if dist[k][j] < math.inf:
              dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])

    for s, t in zip(source, target):
      if s == t:
        continue
      u = ord(s) - ord('a')
      v = ord(t) - ord('a')
      if dist[u][v] == math.inf:
        return -1
      ans += dist[u][v]

    return ans

class Solution {
  public long minimumCost(String source, String target, char[] original, char[] changed,
                          int[] cost) {
    long ans = 0;
    // dist[u][v] := the minimum distance to change ('a' + u) to ('a' + v)
    long[][] dist = new long[26][26];
    Arrays.stream(dist).forEach(A -> Arrays.fill(A, Long.MAX_VALUE));

    for (int i = 0; i < cost.length; ++i) {
      final int u = original[i] - 'a';
      final int v = changed[i] - 'a';
      dist[u][v] = Math.min(dist[u][v], cost[i]);
    }

    for (int k = 0; k < 26; ++k)
      for (int i = 0; i < 26; ++i)
        if (dist[i][k] < Long.MAX_VALUE)
          for (int j = 0; j < 26; ++j)
            if (dist[k][j] < Long.MAX_VALUE)
              dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);

    for (int i = 0; i < source.length(); ++i) {
      if (source.charAt(i) == target.charAt(i))
        continue;
      final int u = source.charAt(i) - 'a';
      final int v = target.charAt(i) - 'a';
      if (dist[u][v] == Long.MAX_VALUE)
        return -1;
      ans += dist[u][v];
    }

    return ans;
  }
}

class Solution {
 public:
  long long minimumCost(string source, string target, vector<char>& original,
                        vector<char>& changed, vector<int>& cost) {
    long ans = 0;
    // dist[u][v] := the minimum distance to change ('a' + u) to ('a' + v)
    vector<vector<long>> dist(26, vector<long>(26, LONG_MAX));

    for (int i = 0; i < cost.size(); ++i) {
      const int u = original[i] - 'a';
      const int v = changed[i] - 'a';
      dist[u][v] = min(dist[u][v], static_cast<long>(cost[i]));
    }

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

    for (int i = 0; i < source.length(); ++i) {
      if (source[i] == target[i])
        continue;
      const int u = source[i] - 'a';
      const int v = target[i] - 'a';
      if (dist[u][v] == LONG_MAX)
        return -1;
      ans += dist[u][v];
    }

    return ans;
  }
};


You are given two 0-indexed strings `source` and `target`, both of length `n`
and consisting of lowercase English characters. You are also given two
0-indexed string arrays `original` and `changed`, and an integer array `cost`,
where `cost[i]` represents the cost of converting the string `original[i]` to
the string `changed[i]`.

You start with the string `source`. In one operation, you can pick a substring
`x` from the string, and change it to `y` at a cost of `z` if there exists any
index `j` such that `cost[j] == z`, `original[j] == x`, and `changed[j] == y`.
You are allowed to do any number of operations, but any pair of operations
must satisfy either of these two conditions:

* The substrings picked in the operations are `source[a..b]` and `source[c..d]` with either `b < c` or `d < a`. In other words, the indices picked in both operations are disjoint.
* The substrings picked in the operations are `source[a..b]` and `source[c..d]` with `a == c` and `b == d`. In other words, the indices picked in both operations are identical.

Return the minimum cost to convert the string `source` to the string `target`
using any number of operations. If it is impossible to convert `source` to
`target`, return `-1`.

Note that there may exist indices `i`, `j` such that `original[j] ==
original[i]` and `changed[j] == changed[i]`.

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 minimumCost(self, source: str, target: str, original: List[str], changed: List[str], cost: List[int]) -> int:
    subLengths = set(len(s) for s in original)
    subToId = self._getSubToId(original, changed)
    subCount = len(subToId)
    dist = [[math.inf for _ in range(subCount)] for _ in range(subCount)]
    dp = [math.inf for _ in range(len(source) + 1)]

    for a, b, c in zip(original, changed, cost):
      u = subToId[a]
      v = subToId[b]
      dist[u][v] = min(dist[u][v], c)

    for k in range(subCount):
      for i in range(subCount):
        if dist[i][k] < math.inf:
          for j in range(subCount):
            if dist[k][j] < math.inf:
              dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])

    dp[0] = 0

    for i, (s, t) in enumerate(zip(source, target)):
      if dp[i] == math.inf:
        continue
      if s == t:
        dp[i + 1] = min(dp[i + 1], dp[i])
      for subLength in subLengths:
        if i + subLength > len(source):
          continue
        subSource = source[i:i + subLength]
        subTarget = target[i:i + subLength]
        if subSource not in subToId or subTarget not in subToId:
          continue
        u = subToId[subSource]
        v = subToId[subTarget]
        if dist[u][v] != math.inf:
          dp[i + subLength] = min(dp[i + subLength], dp[i] + dist[u][v])

    if dp[len(source)] == math.inf:
      return -1
    else:
      return dp[len(source)]

  def _getSubToId(self, original: str, changed: str) -> Dict[str, int]:
    subToId = {}
    for s in original + changed:
      if s not in subToId:
        subToId[s] = len(subToId)
    return subToId

class Solution {
  public long minimumCost(String source, String target, String[] original, String[] changed,
                          int[] cost) {
    Set<Integer> subLengths = getSubLengths(original);
    Map<String, Integer> subToId = getSubToId(original, changed);
    final int subCount = subToId.size();
    // dist[u][v] := the minimum distance to change the substring with id u to
    // the substring with id v
    long[][] dist = new long[subCount][subCount];
    Arrays.stream(dist).forEach(A -> Arrays.fill(A, Long.MAX_VALUE));
    // dp[i] := the minimum cost to change the first i letters of `source` into
    // `target`, leaving the suffix untouched
    long[] dp = new long[source.length() + 1];
    Arrays.fill(dp, Long.MAX_VALUE);

    for (int i = 0; i < cost.length; ++i) {
      final int u = subToId.get(original[i]);
      final int v = subToId.get(changed[i]);
      dist[u][v] = Math.min(dist[u][v], (long) cost[i]);
    }

    for (int k = 0; k < subCount; ++k)
      for (int i = 0; i < subCount; ++i)
        if (dist[i][k] < Long.MAX_VALUE)
          for (int j = 0; j < subCount; ++j)
            if (dist[k][j] < Long.MAX_VALUE)
              dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);

    dp[0] = 0;

    for (int i = 0; i < source.length(); ++i) {
      if (dp[i] == Long.MAX_VALUE)
        continue;
      if (target.charAt(i) == source.charAt(i))
        dp[i + 1] = Math.min(dp[i + 1], dp[i]);
      for (int subLength : subLengths) {
        if (i + subLength > source.length())
          continue;
        String subSource = source.substring(i, i + subLength);
        String subTarget = target.substring(i, i + subLength);
        if (!subToId.containsKey(subSource) || !subToId.containsKey(subTarget))
          continue;
        final int u = subToId.get(subSource);
        final int v = subToId.get(subTarget);
        if (dist[u][v] < Long.MAX_VALUE)
          dp[i + subLength] = Math.min(dp[i + subLength], dp[i] + dist[u][v]);
      }
    }

    return dp[source.length()] == Long.MAX_VALUE ? -1 : dp[source.length()];
  }

  private Map<String, Integer> getSubToId(String[] original, String[] changed) {
    Map<String, Integer> subToId = new HashMap<>();
    for (final String s : original)
      subToId.putIfAbsent(s, subToId.size());
    for (final String s : changed)
      subToId.putIfAbsent(s, subToId.size());
    return subToId;
  }

  private Set<Integer> getSubLengths(String[] original) {
    Set<Integer> subLengths = new HashSet<>();
    for (final String s : original)
      subLengths.add(s.length());
    return subLengths;
  }
}

class Solution {
 public:
  long long minimumCost(string source, string target, vector<string>& original,
                        vector<string>& changed, vector<int>& cost) {
    const unordered_set<int> subLengths = getSubLengths(original);
    const unordered_map<string, int> subToId = getSubToId(original, changed);
    const int subCount = subToId.size();
    // dist[u][v] := the minimum distance to change the substring with id u to
    // the substring with id v
    vector<vector<long>> dist(subCount, vector<long>(subCount, LONG_MAX));
    // dp[i] := the minimum cost to change the first i letters of `source` into
    // `target`, leaving the suffix untouched
    vector<long> dp(source.length() + 1, LONG_MAX);

    for (int i = 0; i < cost.size(); ++i) {
      const int u = subToId.at(original[i]);
      const int v = subToId.at(changed[i]);
      dist[u][v] = min(dist[u][v], static_cast<long>(cost[i]));
    }

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

    dp[0] = 0;

    for (int i = 0; i < source.length(); ++i) {
      if (dp[i] == LONG_MAX)
        continue;
      if (target[i] == source[i])
        dp[i + 1] = min(dp[i + 1], dp[i]);
      for (const int subLength : subLengths) {
        if (i + subLength > source.length())
          continue;
        const string subSource = source.substr(i, subLength);
        const string subTarget = target.substr(i, subLength);
        if (!subToId.contains(subSource) || !subToId.contains(subTarget))
          continue;
        const int u = subToId.at(subSource);
        const int v = subToId.at(subTarget);
        if (dist[u][v] < LONG_MAX)
          dp[i + subLength] = min(dp[i + subLength], dp[i] + dist[u][v]);
      }
    }

    return dp[source.length()] == LONG_MAX ? -1 : dp[source.length()];
  }

 private:
  unordered_map<string, int> getSubToId(const vector<string>& original,
                                        const vector<string>& changed) {
    unordered_map<string, int> subToId;
    for (const string& s : original)
      if (!subToId.contains(s))
        subToId[s] = subToId.size();
    for (const string& s : changed)
      if (!subToId.contains(s))
        subToId[s] = subToId.size();
    return subToId;
  }

  unordered_set<int> getSubLengths(const vector<string>& original) {
    unordered_set<int> subLengths;
    for (const string& s : original)
      subLengths.insert(s.length());
    return subLengths;
  }
};


You are given a 0-indexed string `s` having an even length `n`.

You are also given a 0-indexed 2D integer array, `queries`, where `queries[i]
= [ai, bi, ci, di]`.

For each query `i`, you are allowed to perform the following operations:

* Rearrange the characters within the substring `s[ai:bi]`, where `0 <= ai <= bi < n / 2`.
* Rearrange the characters within the substring `s[ci:di]`, where `n / 2 <= ci <= di < n`.

For each query, your task is to determine whether it is possible to make `s` a
palindrome by performing the operations.

Each query is answered independently of the others.

Return a 0-indexed array `answer`, where `answer[i] == true` if it is possible
to make `s` a palindrome by performing operations specified by the `ith`
query, and `false` otherwise.

* A substring is a contiguous sequence of characters within a string.
* `s[x:y]` represents the substring consisting of characters from the index `x` to index `y` in `s`, both inclusive.

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 canMakePalindromeQueries(self, s: str, queries: List[List[int]]) -> List[bool]:
    n = len(s)
    mirroredDiffs = self._getMirroredDiffs(s)
    counts = self._getCounts(s)
    ans = []

    def subtractArrays(a: List[int], b: List[int]):
      return [x - y for x, y in zip(a, b)]

    for a, b, c, d in queries:
      b += 1
      d += 1
      ra = n - a
      rb = n - b
      rc = n - c
      rd = n - d

      if (min(a, rd) > 0 and mirroredDiffs[min(a, rd)] > 0) or (n // 2 > max(b, rc) and mirroredDiffs[n // 2] - mirroredDiffs[max(b, rc)] > 0) or (rd > b and mirroredDiffs[rd] - mirroredDiffs[b] > 0) or (a > rc and mirroredDiffs[a] - mirroredDiffs[rc] > 0):
        ans.append(False)
      else:
        leftRangeCount = subtractArrays(counts[b], counts[a])
        rightRangeCount = subtractArrays(counts[d], counts[c])
        if a > rd:
          rightRangeCount = subtractArrays(
              rightRangeCount, subtractArrays(counts[min(a, rc)], counts[rd]))
        if rc > b:
          rightRangeCount = subtractArrays(
              rightRangeCount, subtractArrays(counts[rc], counts[max(b, rd)]))
        if c > rb:
          leftRangeCount = subtractArrays(
              leftRangeCount, subtractArrays(counts[min(c, ra)], counts[rb]))
        if ra > d:
          leftRangeCount = subtractArrays(
              leftRangeCount, subtractArrays(counts[ra], counts[max(d, rb)]))
        ans.append(min(leftRangeCount) >= 0
                   and min(rightRangeCount) >= 0
                   and leftRangeCount == rightRangeCount)

    return ans

  def _getMirroredDiffs(self, s: str) -> List[int]:
    diffs = [0]
    for i, j in zip(range(len(s)), reversed(range(len(s)))):
      if i >= j:
        break
      diffs.append(diffs[-1] + (s[i] != s[j]))
    return diffs

  def _getCounts(self, s: str) -> List[List[int]]:
    count = [0] * 26
    counts = [count.copy()]
    for c in s:
      count[ord(c) - ord('a')] += 1
      counts.append(count.copy())
    return counts

class Solution {
  public boolean[] canMakePalindromeQueries(String s, int[][] queries) {
    final int n = s.length();
    // mirroredDiffs[i] := the number of different letters between the first i
    // letters of s[0..n / 2) and the first i letters of s[n / 2..n)[::-1]
    final int[] mirroredDiffs = getMirroredDiffs(s);
    // counts[i] := the count of s[0..i)
    final int[][] counts = getCounts(s);
    boolean[] ans = new boolean[queries.length];

    for (int i = 0; i < queries.length; i++) {
      // Use left-closed, right-open intervals to facilitate the calculation.
      //   ...... [a, b) ...|... [rb, ra) ......
      //   .... [rd, rc) .....|..... [c, d) ....
      int[] query = queries[i];
      final int a = query[0];
      final int b = query[1] + 1;
      final int c = query[2];
      final int d = query[3] + 1;
      final int ra = n - a; // the reflected index of a in s[n / 2..n)
      final int rb = n - b; // the reflected index of b in s[n / 2..n)
      final int rc = n - c; // the reflected index of c in s[n / 2..n)
      final int rd = n - d; // the reflected index of d in s[n / 2..n)
      // No difference is allowed outside the query ranges.
      if ((Math.min(a, rd) > 0 && mirroredDiffs[Math.min(a, rd)] > 0) ||
          (n / 2 > Math.max(b, rc) && mirroredDiffs[n / 2] - mirroredDiffs[Math.max(b, rc)] > 0) ||
          (rd > b && mirroredDiffs[rd] - mirroredDiffs[b] > 0) ||
          (a > rc && mirroredDiffs[a] - mirroredDiffs[rc] > 0)) {
        ans[i] = false;
      } else {
        // The `count` map of the intersection of [a, b) and [rd, rc) in
        // s[0..n / 2) must equate to the `count` map of the intersection of
        // [c, d) and [rb, ra) in s[n / 2..n).
        int[] leftRangeCount = subtractArrays(counts[b], counts[a]);
        int[] rightRangeCount = subtractArrays(counts[d], counts[c]);
        if (a > rd)
          rightRangeCount =
              subtractArrays(rightRangeCount, subtractArrays(counts[Math.min(a, rc)], counts[rd]));
        if (rc > b)
          rightRangeCount =
              subtractArrays(rightRangeCount, subtractArrays(counts[rc], counts[Math.max(b, rd)]));
        if (c > rb)
          leftRangeCount =
              subtractArrays(leftRangeCount, subtractArrays(counts[Math.min(c, ra)], counts[rb]));
        if (ra > d)
          leftRangeCount =
              subtractArrays(leftRangeCount, subtractArrays(counts[ra], counts[Math.max(d, rb)]));
        ans[i] = Arrays.stream(leftRangeCount).allMatch(freq -> freq >= 0) &&
                 Arrays.stream(rightRangeCount).allMatch(freq -> freq >= 0) &&
                 Arrays.equals(leftRangeCount, rightRangeCount);
      }
    }

    return ans;
  }

  private int[] getMirroredDiffs(final String s) {
    int[] diffs = new int[s.length() / 2 + 1];
    for (int i = 0, j = s.length() - 1; i < j; i++, j--) {
      diffs[i + 1] = diffs[i] + (s.charAt(i) != s.charAt(j) ? 1 : 0);
    }
    return diffs;
  }

  private int[][] getCounts(final String s) {
    int[][] counts = new int[s.length() + 1][26];
    int[] count = new int[26];
    for (int i = 0; i < s.length(); ++i) {
      ++count[s.charAt(i) - 'a'];
      System.arraycopy(count, 0, counts[i + 1], 0, 26);
    }
    return counts;
  }

  private int[] subtractArrays(int[] a, int[] b) {
    int[] res = new int[a.length];
    for (int i = 0; i < a.length; ++i)
      res[i] = a[i] - b[i];
    return res;
  }
}

class Solution {
 public:
  vector<bool> canMakePalindromeQueries(string s,
                                        vector<vector<int>>& queries) {
    const int n = s.length();
    // mirroredDiffs[i] := the number of different letters between the first i
    // letters of s[0..n / 2) and the first i letters of s[n / 2..n)[::-1]
    const vector<int> mirroredDiffs = getMirroredDiffs(s);
    // counts[i] := the count of s[0..i)
    const vector<vector<int>> counts = getCounts(s);
    vector<bool> ans;

    for (const vector<int>& query : queries) {
      // Use left-closed, right-open intervals to facilitate the calculation.
      //   ...... [a, b) ...|... [rb, ra) ......
      //   .... [rd, rc) .....|..... [c, d) ....
      const int a = query[0];
      const int b = query[1] + 1;
      const int c = query[2];
      const int d = query[3] + 1;
      const int ra = n - a;  // the reflected index of a in s[n / 2..n)
      const int rb = n - b;  // the reflected index of b in s[n / 2..n)
      const int rc = n - c;  // the reflected index of c in s[n / 2..n)
      const int rd = n - d;  // the reflected index of d in s[n / 2..n)
      // No difference is allowed outside the query ranges.
      if (min(a, rd) > 0 && mirroredDiffs[min(a, rd)] > 0 ||
          n / 2 > max(b, rc) &&
              mirroredDiffs[n / 2] - mirroredDiffs[max(b, rc)] > 0 ||
          rd > b && mirroredDiffs[rd] - mirroredDiffs[b] > 0 ||
          a > rc && mirroredDiffs[a] - mirroredDiffs[rc] > 0) {
        ans.push_back(false);
      } else {
        // The `count` map of the intersection of [a, b) and [rd, rc) in
        // s[0..n / 2) must equate to the `count` map of the intersection of
        // [c, d) and [rb, ra) in s[n / 2..n).
        vector<int> leftRangeCount = subtractArrays(counts[b], counts[a]);
        vector<int> rightRangeCount = subtractArrays(counts[d], counts[c]);
        if (a > rd)
          rightRangeCount = subtractArrays(
              rightRangeCount, subtractArrays(counts[min(a, rc)], counts[rd]));
        if (rc > b)
          rightRangeCount = subtractArrays(
              rightRangeCount, subtractArrays(counts[rc], counts[max(b, rd)]));
        if (c > rb)
          leftRangeCount = subtractArrays(
              leftRangeCount, subtractArrays(counts[min(c, ra)], counts[rb]));
        if (ra > d)
          leftRangeCount = subtractArrays(
              leftRangeCount, subtractArrays(counts[ra], counts[max(d, rb)]));
        ans.push_back(ranges::all_of(leftRangeCount, [](int freq) {
          return freq >= 0;
        }) && ranges::all_of(rightRangeCount, [](int freq) {
          return freq >= 0;
        }) && leftRangeCount == rightRangeCount);
      }
    }

    return ans;
  }

 private:
  vector<int> getMirroredDiffs(const string& s) {
    vector<int> diffs(1);
    for (int i = 0, j = s.length() - 1; i < j; ++i, --j)
      diffs.push_back(diffs.back() + (s[i] != s[j] ? 1 : 0));
    return diffs;
  }

  vector<vector<int>> getCounts(const string& s) {
    vector<int> count(26);
    vector<vector<int>> counts{count};
    for (const char c : s) {
      ++count[c - 'a'];
      counts.push_back(count);
    }
    return counts;
  }

  vector<int> subtractArrays(const vector<int>& a, const vector<int>& b) {
    vector<int> res;
    for (int i = 0; i < a.size(); ++i)
      res.push_back(a[i] - b[i]);
    return res;
  }
};