Skip to main content
Back to problems
#2983
Hard Algorithms

Palindrome rearrangement queries

Hash Table String Prefix Sum
24.8% acceptance
Feb 25, 2026
98
27
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.

Solution

Rust
Time O(n²)
Space O(n)
LeetCode
solution.rs
impl Solution {
  pub fn can_make_palindrome_queries(s: String, queries: Vec<Vec<i32>>) -> Vec<bool> {
    let n = s.len();
    let half = n / 2;
    let bs = s.as_bytes();

    // pf[i][c] = count of char c in s[0..i-1] (first half, i in 0..=half)
    // pr[i][c] = count of char c in s[n-1..n-1-i] (second-half mirrors of first-half [0..i-1])
    //           i.e., pr[i+1][c] = pr[i][c] + count(s[n-1-i] == c)
    // mm[i] = count of mismatched pairs (j, n-1-j) for j in [0, i-1]
    let mut pf = vec![[0i32; 26]; half + 1];
    let mut pr = vec![[0i32; 26]; half + 1];
    let mut mm = vec![0i32; half + 1];

    for i in 0..half {
      pf[i + 1] = pf[i];
      pf[i + 1][(bs[i] - b'a') as usize] += 1;

      pr[i + 1] = pr[i];
      pr[i + 1][(bs[n - 1 - i] - b'a') as usize] += 1;

      mm[i + 1] = mm[i] + if bs[i] != bs[n - 1 - i] { 1 } else { 0 };
    }

    let freq_first = |a: usize, b: usize| -> [i32; 26] {
      let mut f = [0i32; 26];
      for c in 0..26 { f[c] = pf[b + 1][c] - pf[a][c]; }
      f
    };

    // freq of second-half mirrors of first-half positions [a..b]
    let freq_mir = |a: usize, b: usize| -> [i32; 26] {
      let mut f = [0i32; 26];
      for c in 0..26 { f[c] = pr[b + 1][c] - pr[a][c]; }
      f
    };

    let mm_range = |a: usize, b: usize| -> i32 {
      if a > b { 0 } else { mm[b + 1] - mm[a] }
    };

    let mut result = Vec::with_capacity(queries.len());

    for q in &queries {
      let (a, b) = (q[0] as usize, q[1] as usize);
      let (c, d) = (q[2] as usize, q[3] as usize);

      // Mirror of second-half [c, d] in first-half: [n-1-d, n-1-c]
      let mr_lo = n - 1 - d;
      let mr_hi = n - 1 - c;

      // Check mismatches outside L=[a,b] ∪ mirR=[mr_lo, mr_hi]
      let total_mm = mm[half];
      let mm_l = mm_range(a, b);
      let mm_mirr = mm_range(mr_lo, mr_hi);
      let ov_lo = a.max(mr_lo);
      let ov_hi = b.min(mr_hi);
      let mm_ov = if ov_lo <= ov_hi { mm_range(ov_lo, ov_hi) } else { 0 };
      let mm_union = mm_l + mm_mirr - mm_ov;

      if total_mm != mm_union {
        result.push(false);
        continue;
      }

      // Character availability check
      // fl: characters available from first-half [a,b] (can be rearranged)
      let fl = freq_first(a, b);
      // fr: characters available from second-half mirrors of [mr_lo, mr_hi]
      let fr = freq_mir(mr_lo, mr_hi);

      // Required from fl: second-half chars at mirror positions of (L \ mirR)
      // Positions in L but not mirR: [a, min(b, mr_lo-1)] and [max(a, mr_hi+1), b]
      let mut req_l = [0i32; 26];
      // part of L before mirR: [a, mr_lo-1]
      if a < mr_lo {
        let hi = (mr_lo - 1).min(b);
        if a <= hi {
          let f = freq_mir(a, hi);
          for c in 0..26 { req_l[c] += f[c]; }
        }
      }
      // part of L after mirR: [max(a, mr_hi+1), b]
      if mr_hi < b {
        let lo = (mr_hi + 1).max(a);
        if lo <= b {
          let f = freq_mir(lo, b);
          for c in 0..26 { req_l[c] += f[c]; }
        }
      }

      // Required from fr: first-half chars at (mirR \ L) positions
      // Positions in mirR but not L: [mr_lo, a-1] and [b+1, mr_hi]
      let mut req_r = [0i32; 26];
      // part of mirR before L: [mr_lo, a-1]
      if mr_lo < a {
        let hi = (a - 1).min(mr_hi);
        if mr_lo <= hi {
          let f = freq_first(mr_lo, hi);
          for c in 0..26 { req_r[c] += f[c]; }
        }
      }
      // part of mirR after L: [max(mr_lo, b+1), mr_hi]
      if b < mr_hi {
        let lo = (b + 1).max(mr_lo);
        if lo <= mr_hi {
          let f = freq_first(lo, mr_hi);
          for c in 0..26 { req_r[c] += f[c]; }
        }
      }

      // Check fl can provide req_l and fr can provide req_r
      let mut leftover_l = fl;
      let mut leftover_r = fr;
      let mut ok = true;
      for c in 0..26 {
        leftover_l[c] -= req_l[c];
        if leftover_l[c] < 0 { ok = false; break; }
      }
      if ok {
        for c in 0..26 {
          leftover_r[c] -= req_r[c];
          if leftover_r[c] < 0 { ok = false; break; }
        }
      }
      if ok {
        for c in 0..26 {
          if leftover_l[c] != leftover_r[c] { ok = false; break; }
        }
      }

      result.push(ok);
    }

    result
  }
}