![]() |
VOOZH | about |
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.
Table of Content
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.
1 0 0 1
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:
Step by Step Implementation:
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)
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:
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.