CSES Solution - Palindrome Queries
Last Updated :
28 Mar, 2024
You are given a string that consists of n characters between a–z. The positions of the string are indexed 1,2,...n.
Your task is to process m operations of the following types:
- Change the character at position k to x
- Check if the substring from position a to position b is a palindrome
Input: Each operations are in the form of "1 k x" or "2 a b".
Example:
Input: S = aybabtu, m = {{2, 3, 5}, {1, 3, 'x'}, {2, 3, 5}, {1, 5, 'x'}, {2, 3, 5}}
Output:
YES
NO
YES
Input: S = "racecar", m = { {1, 2, 'a'}, {2, 0, 6}, {1, 3, 'e'}, {2, 0, 6}}
Output:
NO
NO
Approach:
The idea is to hash table to store the forward and backward representations of the string. Each character in the string is mapped with a unique prime number (hash), and the representation of a substring is the product of the hashes of its characters. This allows us to compute the representation of any substring in constant time, given the representations of its prefix and suffix.
Lets breakdown step-by-step solution:
We will do Preprocessing: We first compute the powers of a fixed hash for each position in the string, and initialize two hash tables to store the forward and backward representations of the string.
We'll Updating a Character: When the operation is to change a character, we update the forward and backward hash tables at the corresponding position. This is done by multiplying the old hash at the position by the new hash, and updating the hash tables.
Checking a Substring: When the operation is to check if a substring is a palindrome, we query the forward and backward hash tables for the substring, and adjust the hashes by the appropriate powers to align the substring with the start of the string. If the adjusted forward and backward hashes are equal, then the substring is a palindrome.
Performing the Operations: We perform each operation in the order given. For each operation, we either update a character or check a substring as described above.
Step-by-step approach:
- Create constants HASH and MOD for hashing.
- Initialize arrays hashPower[], fwdHashTable[], and bckHashTable[] for hashing operations.
- Implement functions to update and query hash values in the forward and backward hash tables:
- updatefwd and queryfwd functions for the forward hash table.
- updatebck and querybck functions for the backward hash table.
- Our main function:
- Perform each operation:
- If the operation type is 1:
- Update the forward and backward hash tables for a specific position with a new character.
- If the operation type is 2:
- Query forward and backward hash tables for a substring defined by left and right positions.
- Check if the substring is a palindrome by comparing the forward and backward hash values.
- Output "YES" if it's a palindrome, otherwise output "NO".
Below is the implementation of the above approach:
Java
import java.util.*;
public class Main {
// Define HASH and MOD constants
static final long HASH = 257;
static final long MOD = 1000000007;
// Length of the string and number of operations
static int n, m;
// Array to store powers of HASH
static long[] hashPower = new long[200005];
// Forward hash table
static long[] fwdHashTable = new long[400005];
// Backward hash table
static long[] bckHashTable = new long[400005];
// Function to update the hash value at position i in the forward hash table
static void updatefwd(int i, long v) {
for (fwdHashTable[i += n] = v; i > 1; i >>= 1)
fwdHashTable[i >> 1] = (fwdHashTable[i] + fwdHashTable[i ^ 1]) % MOD;
}
// Function to query the hash value from position l to r in the forward hash table
static long queryfwd(int l, int r) {
long res = 0;
for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1) {
if ((l & 1) == 1) res = (res + fwdHashTable[l++]) % MOD;
if ((r & 1) == 1) res = (res + fwdHashTable[--r]) % MOD;
}
return res;
}
// Function to update the hash value at position i in the backward hash table
static void updatebck(int i, long v) {
for (bckHashTable[i += n] = v; i > 1; i >>= 1)
bckHashTable[i >> 1] = (bckHashTable[i] + bckHashTable[i ^ 1]) % MOD;
}
// Function to query the hash value from position l to r in the backward hash table
static long querybck(int l, int r) {
long res = 0;
for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1) {
if ((l & 1) == 1) res = (res + bckHashTable[l++]) % MOD;
if ((r & 1) == 1) res = (res + bckHashTable[--r]) % MOD;
}
return res;
}
public static void main(String[] args) {
n = 7;
m = 5;
String s = "aybabtu";
// Operations
List<int[]> operations = Arrays.asList(new int[]{2, 3, 5}, new int[]{1, 3, 'x'}, new int[]{2, 3, 5}, new int[]{1, 5, 'x'}, new int[]{2, 3, 5});
// Initialize hash powers for hashing
hashPower[0] = 1;
for (int i = 1; i < n; i++)
hashPower[i] = (hashPower[i - 1] * HASH) % MOD;
// Initialize forward and backward hash tables with the input string
for (int i = 0; i < n; i++) {
char c = s.charAt(i);
updatefwd(i, (hashPower[i] * c) % MOD);
updatebck(i, (hashPower[n - i - 1] * c) % MOD);
}
// Perform each operation
for (int[] op : operations) {
int opType = op[0];
if (opType == 1) { // If operation is type 1
int position = op[1];
char newChar = (char)op[2];
position--;
// Update forward and backward hash tables for position
updatefwd(position, (hashPower[position] * newChar) % MOD);
updatebck(position, (hashPower[n - position - 1] * newChar) % MOD);
} else if (opType == 2) { // If operation is type 2
int left = op[1], right = op[2];
left--; right--;
// Query forward and backward hash tables for substring
long fwd = queryfwd(left, right);
fwd = (fwd * hashPower[n - 1 - right]) % MOD;
long bck = querybck(left, right);
bck = (bck * hashPower[left]) % MOD;
// Check if substring is palindrome
if (fwd == bck) System.out.println("YES");
else System.out.println("NO");
}
}
}
}
// This code is Contributed by Ayush Mishra
Python
# Define HASH and MOD constants
HASH = 257
MOD = 1000000007
# Length of the string and number of operations
n, m = 7, 5
# Array to store powers of HASH
hashPower = [0] * 200005
# Forward hash table
fwdHashTable = [0] * 400005
# Backward hash table
bckHashTable = [0] * 400005
# Function to update the hash value at position i in the forward hash table
def updatefwd(i, v):
i += n
fwdHashTable[i] = v
while i > 1:
fwdHashTable[i >> 1] = (fwdHashTable[i] + fwdHashTable[i ^ 1]) % MOD
i >>= 1
# Function to query the hash value from position l to r in the forward hash table
def queryfwd(l, r):
res = 0
l += n
r += n + 1
while l < r:
if l & 1 == 1:
res = (res + fwdHashTable[l]) % MOD
l += 1
if r & 1 == 1:
r -= 1
res = (res + fwdHashTable[r]) % MOD
l >>= 1
r >>= 1
return res
# Function to update the hash value at position i in the backward hash table
def updatebck(i, v):
i += n
bckHashTable[i] = v
while i > 1:
bckHashTable[i >> 1] = (bckHashTable[i] + bckHashTable[i ^ 1]) % MOD
i >>= 1
# Function to query the hash value from position l to r in the backward hash table
def querybck(l, r):
res = 0
l += n
r += n + 1
while l < r:
if l & 1 == 1:
res = (res + bckHashTable[l]) % MOD
l += 1
if r & 1 == 1:
r -= 1
res = (res + bckHashTable[r]) % MOD
l >>= 1
r >>= 1
return res
n = 7
m = 5
s = "aybabtu"
# Operations
operations = [[2, 3, 5], [1, 3, 'x'], [2, 3, 5], [1, 5, 'x'], [2, 3, 5]]
# Initialize hash powers for hashing
hashPower[0] = 1
for i in range(1, n):
hashPower[i] = (hashPower[i - 1] * HASH) % MOD
# Initialize forward and backward hash tables with the input string
for i in range(n):
c = s[i]
updatefwd(i, (hashPower[i] * ord(c)) % MOD)
updatebck(i, (hashPower[n - i - 1] * ord(c)) % MOD)
# Perform each operation
for op in operations:
opType = op[0]
if opType == 1: # If operation is type 1
position, newChar = op[1] - 1, op[2]
updatefwd(position, (hashPower[position] * ord(newChar)) % MOD)
updatebck(position, (hashPower[n - position - 1] * ord(newChar)) % MOD)
elif opType == 2: # If operation is type 2
left, right = op[1] - 1, op[2] - 1
fwd = queryfwd(left, right)
fwd = (fwd * hashPower[n - 1 - right]) % MOD
bck = querybck(left, right)
bck = (bck * hashPower[left]) % MOD
if fwd == bck:
print("YES")
else:
print("NO")
JavaScript
// Define HASH and MOD constants
const HASH = 257;
const MOD = 1000000007;
// Length of the string and number of operations
let n = 7;
let m = 5;
// Array to store powers of HASH
let hashPower = new Array(200005).fill(0);
// Forward hash table
let fwdHashTable = new Array(400005).fill(0);
// Backward hash table
let bckHashTable = new Array(400005).fill(0);
// Function to update the hash value at position i in the forward hash table
function updatefwd(i, v) {
i += n;
fwdHashTable[i] = v;
while (i > 1) {
fwdHashTable[i >> 1] = (fwdHashTable[i] + fwdHashTable[i ^ 1]) % MOD;
i >>= 1;
}
}
// Function to query the hash value from position l to r in the forward hash table
function queryfwd(l, r) {
let res = 0;
l += n;
r += n + 1;
while (l < r) {
if (l & 1 === 1) {
res = (res + fwdHashTable[l]) % MOD;
l += 1;
}
if (r & 1 === 1) {
r -= 1;
res = (res + fwdHashTable[r]) % MOD;
}
l >>= 1;
r >>= 1;
}
return res;
}
// Function to update the hash value at position i in the backward hash table
function updatebck(i, v) {
i += n;
bckHashTable[i] = v;
while (i > 1) {
bckHashTable[i >> 1] = (bckHashTable[i] + bckHashTable[i ^ 1]) % MOD;
i >>= 1;
}
}
// Function to query the hash value from position l to r in the backward hash table
function querybck(l, r) {
let res = 0;
l += n;
r += n + 1;
while (l < r) {
if (l & 1 === 1) {
res = (res + bckHashTable[l]) % MOD;
l += 1;
}
if (r & 1 === 1) {
r -= 1;
res = (res + bckHashTable[r]) % MOD;
}
l >>= 1;
r >>= 1;
}
return res;
}
n = 7;
m = 5;
let s = "aybabtu";
// Operations
let operations = [[2, 3, 5], [1, 3, 'x'], [2, 3, 5], [1, 5, 'x'], [2, 3, 5]];
// Initialize hash powers for hashing
hashPower[0] = 1;
for (let i = 1; i < n; i++) {
hashPower[i] = (hashPower[i - 1] * HASH) % MOD;
}
// Initialize forward and backward hash tables with the input string
for (let i = 0; i < n; i++) {
let c = s.charCodeAt(i);
updatefwd(i, (hashPower[i] * c) % MOD);
updatebck(i, (hashPower[n - i - 1] * c) % MOD);
}
// Perform each operation
for (let op of operations) {
let opType = op[0];
if (opType === 1) { // If operation is type 1
let position = op[1] - 1;
let newChar = op[2].charCodeAt(0);
updatefwd(position, (hashPower[position] * newChar) % MOD);
updatebck(position, (hashPower[n - position - 1] * newChar) % MOD);
} else if (opType === 2) { // If operation is type 2
let left = op[1] - 1;
let right = op[2] - 1;
let fwd = queryfwd(left, right);
fwd = (fwd * hashPower[n - 1 - right]) % MOD;
let bck = querybck(left, right);
bck = (bck * hashPower[left]) % MOD;
if (fwd === bck) {
console.log("YES");
} else {
console.log("NO");
}
}
}
C++14
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll HASH = 257, MOD = 1e9 + 7;
int n, m;
ll hashPower[200005] = {1};
ll fwdHashTable[400005];
ll bckHashTable[400005];
void updatefwd(int i, ll v) {
for (fwdHashTable[i += n] = v; i > 1; i >>= 1)
fwdHashTable[i >> 1] = (fwdHashTable[i] + fwdHashTable[i ^ 1]) % MOD;
}
ll queryfwd(int l, int r) {
ll res = 0;
for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1) {
if (l & 1)
res = (res + fwdHashTable[l++]) % MOD;
if (r & 1)
res = (res + fwdHashTable[--r]) % MOD;
}
return res;
}
void updatebck(int i, ll v) {
for (bckHashTable[i += n] = v; i > 1; i >>= 1)
bckHashTable[i >> 1] = (bckHashTable[i] + bckHashTable[i ^ 1]) % MOD;
}
ll querybck(int l, int r) {
ll res = 0;
for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1) {
if (l & 1)
res = (res + bckHashTable[l++]) % MOD;
if (r & 1)
res = (res + bckHashTable[--r]) % MOD;
}
return res;
}
int main() {
n = 7;
m = 5;
string s = "aybabtu";
vector<vector<int>> operations = {{2, 3, 5},
{1, 3, 'x'},
{2, 3, 5},
{1, 5, 'x'},
{2, 3, 5}};
for (int i = 1; i < n; i++) {
hashPower[i] = (hashPower[i - 1] * HASH) % MOD;
}
for (int i = 0; i < n; i++) {
char c = s[i];
updatefwd(i, hashPower[i] * (ll)c % MOD);
updatebck(i, hashPower[n - i - 1] * (ll)c % MOD);
}
for (auto &op : operations) {
int opType = op[0];
if (opType == 1) {
int position = op[1];
char newChar = op[2];
position--;
updatefwd(position, hashPower[position] * (ll)newChar % MOD);
updatebck(position, hashPower[n - position - 1] * (ll)newChar % MOD);
} else if (opType == 2) {
int left = op[1], right = op[2];
left--, right--;
ll fwd = queryfwd(left, right);
fwd = (fwd * hashPower[n - 1 - right]) % MOD;
ll bck = querybck(left, right);
bck = (bck * hashPower[left]) % MOD;
if (fwd == bck)
cout << "YES\n";
else
cout << "NO\n";
}
}
return 0;
}
Time Complexity: O(n log n)
Auxiliary Space: O(n)
Similar Reads
CSES Solutions - Palindrome Reorder
Given a string S, your task is to reorder its letters in such a way that it becomes a palindrome (i.e., it reads the same forwards and backwards). Examples: Input: S = "AAAACACBA"Output: AAACBCAAAExplanation: We can reorder "AAAACACBA" to "AAACBCAAA" to get a palindrome string. Input: S = "AAABBB"Ou
6 min read
Palindrome Substring Queries
Given a string str of length n and a 2d array queries[][], where each query queries[i] is of type [i, j]. For each query, your task is to check if the substring str[i:j] is a palindrome. Examples : Input: str = "abaaabaaaba"queries[][] = [ [0, 10], [5, 8], [2, 5], [5, 9] ]Output: 1 0 0 1Explanation:
15+ min read
CSES Solution - Longest Palindrome
Prerequisite: Manacherâs Algorithm Given a string, your task is to determine the longest palindromic substring of the string. For example, the longest palindrome in aybabtu is bab. Example: Input: s = aybabtuOutput: bab Input: GeeksgfgGeeksOutput: gfg Approach: The idea is to use Manacherâs algorith
14 min read
Palindrome Substring Queries in JavaScript
Here are the various approaches to check if a substring is a palindrome (a string that reads the same forward and backwards). Using for Loop - Basic Approachfor loop iterates through each substring character from beginning to end for comparing each pair. If all pairs match, the substring is a palind
3 min read
Palindrome String Coding Problems
A string is called a palindrome if the reverse of the string is the same as the original one. Example: âmadamâ, âracecarâ, â12321â. Properties of a Palindrome String:A palindrome string has some properties which are mentioned below: A palindrome string has a symmetric structure which means that the
2 min read
Queries to check if substring[L...R] is palindrome or not
Given a string str and Q queries. Every query consists of two numbers L and R. The task is to print if the sub-string[L...R] is palindrome or not. Examples: Input: str = "abacccde", Q[][] = {{0, 2}, {1, 2}, {2, 4}, {3, 5}} Output: Yes No No Yes Input: str = "abaaab", Q[][] = {{0, 1}, {1, 5}} Output:
13 min read
Next smallest prime palindrome
Given a positive integer N where [Tex]1 \leq N \leq 10^{9} [/Tex]. The task is to find the smallest prime palindrome greater than or equal to N.Examples: Input: 8 Output: 11 Input: 7000000000 Output: 10000500001 Approach: The Naive approach is to loop from N + 1 until we found the next smallest prim
6 min read
Longest Palindromic Substring
Given a string s, the task is to find the longest substring which is a palindrome. If there are multiple answers, then return the first appearing substring. Examples: Input: s = "forgeeksskeegfor" Output: "geeksskeeg"Explanation: There are several possible palindromic substrings like "kssk", "ss", "
12 min read
Palindrome Partitioning
Given a string s, the task is to find the minimum number of cuts needed for palindrome partitioning of the given string. A partitioning of the string is a palindrome partitioning if every sub-string of the partition is a palindrome. Examples: Input: s = "geek" Output: 2 Explanation: We need to make
15+ min read
Shortest Palindromic Substring
Given a string you need to find the shortest palindromic substring of the string. If there are multiple answers output the lexicographically smallest. Examples: Input: zyzz Output:y Input: abab Output: a Naive Approach: The approach is similar to finding the longest palindromic substring. We keep tr
8 min read