VOOZH about

URL: https://www.geeksforgeeks.org/dsa/palindrome-substring-queries/

⇱ Palindrome Substring Queries - GeeksforGeeks


  • Courses
  • Tutorials
  • Interview Prep

Palindrome Substring Queries

Last Updated : 29 Jul, 2025

Given a string s and a 2D list of queries[][], where each queries[i] consists of two integers [left, right]. Each query refers to the substring s[left : right], where both left and right are inclusive (0-based indexing).
For each query, find whether the substring s[left : right] forms a palindrome.

Examples : 

Input: s = "abaaabaaaba", queries[][] = [[0, 10], [5, 8], [2, 5], [5, 9]]
Output: [1, 0, 0, 1]
Explanation: Lets process all the queries one by one:
-> [0, 10]: The substring is "abaaabaaaba" which is a palindrome.
-> [5, 8]: The substring is "baaa" which is not a palindrome.
-> [2, 5]: The substring is “aaab” which is not a palindrome. 
-> [5, 9]: The substring is “baaab” which is a palindrome. 

Input: s = "abdcaaa", queries[][] = [[0, 1], [2, 2], [4, 6]]
Output: [0, 1, 1]
Explanation: Lets process all the queries one by one:
-> [0, 1]: The substring is "ab" which is not a palindrome.
-> [2, 2]: The substring is "d" which is a palindrome.
-> [4, 6]: The substring is “aaa” which is a palindrome. 

[Naive Approach] Brute Force Palindrome Check - O(n * q) Time and O(1) Space

The idea is for each query, extract the substring boundaries and check if it is a palindrome using two pointers one from the start and one from the end.
This approach compares characters directly for each query without any preprocessing or optimization.


Output
1 0 0 1 

[Expected Approach 1] Using Rabin-Karp Rolling Hash

The idea is to use Rabin-Karp’s rolling hash to efficiently check if a substring is a palindrome. We first precompute prefix hashes of the original string s and its reversed version revS. For each query [l, r], we compute the hash of s[l...r] and compare it to the hash of the corresponding substring in revS, which is revS[n - 1 - r ... n - 1 - l], where n is the length of s. If the two hash values are equal, the substring is a palindrome.

Definition: What Does preHash[i] Store ?

preHash[i] stores the hash of the prefix substring input[0 ... i-1] using both base values (base1 and base2).

Mathematically, for both bases:
=> preHash[i][0] = hash of input[0 to i-1] using base1
=> preHash[i][1] = hash of input[0 to i-1] using base2

The hash is computed as: Hash[i] = (input[0] x base^i−1 + input[1] x base^i−2 + .... + input[i−1] x base^0 ) % mod

This is built incrementally using the recurrence:
=> preHash[i + 1][0] = (preHash[i][0] * base1 + input[i]) % mod
=> preHash[i + 1][1] = (preHash[i][1] * base2 + input[i]) % mod

Substring Hash Formula (from getHash method):

To get the hash of the substring from index left to right - 1:
hash = (preHash[right][k] - preHash[left][k] x power[right−left][k]) % mod
=> where k = 0 for base1 and k = 1 for base2
=> So getHash(left, right) returns a pair of hashes: one using base1, one using base2.

For Palindrome Checking:

  • To check if s[l...r] is a palindrome:
  • Build two RollingHash objects — one for s, one for revS
  • Map [l, r] in s to [n - 1 - r, n - 1 - l] in revS
  • Compare getHash(l, r + 1) of s with getHash(n - 1 - r, n - l) of revS
  • If both base1 and base2 hashes match ⇒ substring is a palindrome

Step by Step Implementation:

  1. Create revS by reversing s.
  2. Initialize a RollingHash object for the original string s.
  3. Initialize a RollingHash object for the reversed string revS.
  4. For each query [left, right], define the substring as s[left...right].
  5. Compute the hash of s[left...right] using getHash(left, right + 1) on the s rolling hash.
  6. Compute revLeft = n - 1 - right, where n is the length of s.
  7. Compute revRight = n - left.
  8. Compute the hash of revS[revLeft...revRight - 1] using getHash(revLeft, revRight) on the revS rolling hash.
  9. Compare the two hashes from step 6 and step 9 for both base1 and base2.
  10. If both hashes match, then s[left...right] is a palindrome.
  11. If the hashes do not match, then s[left...right] is not a palindrome.
  12. Repeat steps 4 to 11 for all queries.

Output
1 0 0 1 

Time Complexity: O(n + q), O(n) to precompute prefix hashes and powers, and O(1) for each of the q queries.
Auxiliary Space: O(n)

[Expected Approach 2] Manacher’s Algorithm for Palindromic Substrings

The idea is to use Manacher's Algorithm to preprocess the input string and compute the length of the longest palindromic substring centered at each position.
By inserting separators (like #) between characters, the algorithm uniformly handles even and odd length palindromes.
After preprocessing, for each query [l, r], we map the original indices to the modified string and check if the palindrome radius at the center covers the full substring length.
This allows us to answer each palindrome query in O(1) time after an O(n) preprocessing.

Step by Step Implementation:

  • Preprocess the string by inserting # between every character and adding special sentinels at both ends (@ at the start and $ at the end).
    Example: s = "aba" → ms = "@#a#b#a#$"
  • Run Manacher’s algorithm on this modified string to compute the array p[], where p[i] stores the maximum radius of the palindrome centered at index i in the modified string.
  • For each query [l, r]:
    => Calculate the length of the original substring: len = r - l + 1
    => Convert the original center index to modified string's center: center = l + r + 2 (offset due to inserted # and starting @)
  • Check if the value of p[center] ≥ len. This confirms that the substring s[l..r] is fully covered by the palindrome radius from the center.
  • Return 1 if it's a palindrome, otherwise return 0.

Output
1 0 0 1 

Time Complexity: O(n + q), manacher's algorithm runs in linear time O(n) due to clever use of symmetry and previous computations. Each query check is constant time.
Auxiliary Space: O(n), because we store a modified version of the string (ms) and a radius array (p[]), both proportional to the input size.

Comment