Open In App

CSES Solution – Palindrome Queries

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:

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
# 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")
// 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");
        }
    }
}
#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;
}

Output
YES
NO
YES






Time Complexity: O(n log n)
Auxiliary Space: O(n)

Article Tags :