CSES Solution Palindrome Queries (original) (raw)

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:

**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:

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)