Given a string and several queries on the substrings of the given input string to check whether the substring is a palindrome or not.
Examples :
Suppose our input string is âabaaabaaabaâ and the queries- [0, 10], [5, 8], [2, 5], [5, 9]
We have to tell that the substring having the starting and ending indices as above is a palindrome or not.
[0, 10] → Substring is âabaaabaaabaâ which is a palindrome.
[5, 8] → Substring is âbaaaâ which is not a palindrome.
[2, 5] → Substring is âaaabâ which is not a palindrome.
[5, 9] → Substring is âbaaabâ which is a palindrome.
Let us assume that there are Q such queries to be answered and N be the length of our input string. There are the following two ways to answer these queries
One by one we go through all the substrings of the queries and check whether the substring under consideration is a palindrome or not.
Since there are Q queries and each query can take O(N) worse case time to answer, this method takes O(Q.N) time in the worst case. Although this is an in-place/space-efficient algorithm, still there is a more efficient method to do this.
The idea is similar to Rabin Karp string matching. We use string hashing. What we do is that we calculate cumulative hash values of the string in the original string as well as the reversed string in two arrays- prefix[] and suffix[].
How to calculate the cumulative hash values?
Suppose our string is str[], then the cumulative hash function to fill our prefix[] array used is-
prefix[0] = 0
prefix[i] = str[0] + str[1] * 101 + str[2] * 1012 + …… + str[i-1] * 101i-1For example, take the string- âabaaabxyabaâ
prefix[0] = 0
prefix[1] = 97 (ASCII Value of âaâ is 97)
prefix[2] = 97 + 98 * 101
prefix[3] = 97 + 98 * 101 + 97 * 1012
………………………
………………………
prefix[11] = 97 + 98 * 101 + 97 * 1012 + ……..+ 97 * 10110
Now the reason to store in that way is that we can easily find the hash value of any substring in O(1) time using-
hash(L, R) = prefix[R+1] â prefix[L]
For example, hash (1, 5) = hash (âbaaabâ) = prefix[6] â prefix[1] = 98 * 101 + 97 * 1012 + 97 * 1013 + 97 * 1014 + 98 * 1015 = 1040184646587 [We will use this weird value later to explain whatâs happening].
Similar to this we will fill our suffix[] array as-
suffix[0] = 0
suffix[i] = str[n-1] + str[n-2] * 101 + str[n-3] * 1012 + …… + str[n-i] * 101i-1For example, take the string- âabaaabxyabaâ
suffix[0] = 0
suffix[1] = 97 (ASCII Value of âaâ is 97)
suffix[2] = 97 + 98 * 101
suffix[3] = 97 + 98 * 101 + 97 * 1012
………………………
………………………
suffix[11] = 97 + 98 * 101 + 97 * 1012 + ……..+ 97 * 10110Now the reason to store in that way is that we can easily find the reverse hash value of any substring in O(1) time using
reverse_hash(L, R) = hash (R, L) = suffix[n-L] â suffix[n-R-1]where n = length of string.
For “abaaabxyaba”, n = 11
reverse_hash(1, 5) = reverse_hash(âbaaabâ) = hash(âbaaabâ) [Reversing âbaaabâ gives âbaaabâ]
hash(âbaaabâ) = suffix[11-1] â suffix[11-5-1] = suffix[10] â suffix[5] = 98 * 1015 + 97 * 1016 + 97 * 1017 + 97 * 1018 + 98 * 1019 = 108242031437886501387
Now there doesnât seem to be any relationship between these two weird integers â 1040184646587 and 108242031437886501387
Think again. Is there any relation between these two massive integers ?
Yes, there is and this observation is the core of this program/article.
1040184646587 * 1014 = 108242031437886501387
Try thinking about this and you will find that any substring starting at index- L and ending at index- R (both inclusive) will be a palindrome if
(prefix[R + 1] â prefix[L]) / (101L) = (suffix [n – L] â suffix [n â R- 1] ) / (101n â R – 1)
The rest part is just implementation.
The function computerPowers() in the program computes the powers of 101 using dynamic programming.
Overflow Issues:
As, we can see that the hash values and the reverse hash values can become huge for even the small strings of length â 8. Since C and C++ doesnât provide support for such large numbers, so it will cause overflows. To avoid this we will take modulo of a prime (a prime number is chosen for some specific mathematical reasons). We choose the biggest possible prime which fits in an integer value. The best such value is 1000000007. Hence all the operations are done modulo 1000000007.
However, Java and Python has no such issues and can be implemented without the modulo operator.
The fundamental modulo operations which are used extensively in the program are listed below.
1) Addition-(a + b) %M = (a %M + b % M) % M
(a + b + c) % M = (a % M + b % M + c % M) % M
(a + b + c + d) % M = (a % M + b % M + c % M+ d% M) % M
…. ….. ….. ……
…. ….. ….. ……2) Multiplication-
(a * b) % M = (a * b) % M
(a * b * c) % M = ((a * b) % M * c % M) % M
(a * b * c * d) % M = ((((a * b) % M * c) % M) * d) % M
…. ….. ….. ……
…. ….. ….. ……This property is used by modPow() function which computes power of a number modulo M
3) Mixture of addition and multiplication-(a * x + b * y + c) % M = ( (a * x) % M +(b * y) % M+ c % M ) % M
4) Subtraction-
(a – b) % M = (a % M â b % M + M) % M [Correct]
(a – b) % M = (a % M â b % M) % M [Wrong]5) Division-
(a / b) % M = (a * MMI(b)) % M
Where MMI() is a function to calculate Modulo Multiplicative Inverse. In our program this is implemented by the function- findMMI().
C++
/* A C++ program to answer queries to check whetherthe substrings are palindrome or not efficiently */#include <bits/stdc++.h>usingnamespacestd;#define p 101#define MOD 1000000007// Structure to represent a query. A query consists// of (L, R) and we have to answer whether the substring// from index-L to R is a palindrome or notstructQuery {intL, R;};// A function to check if a string str is palindrome// in the ranfe L to RboolisPalindrome(string str,intL,intR){// Keep comparing characters while they are samewhile(R > L)if(str[L++] != str[R--])return(false);return(true);}// A Function to find pow (base, exponent) % MOD// in log (exponent) timeunsignedlonglongintmodPow(unsignedlonglongintbase,unsignedlonglongintexponent){if(exponent == 0)return1;if(exponent == 1)returnbase;unsignedlonglonginttemp = modPow(base, exponent / 2);if(exponent % 2 == 0)return(temp % MOD * temp % MOD) % MOD;elsereturn(((temp % MOD * temp % MOD) % MOD)* base % MOD)% MOD;}// A Function to calculate Modulo Multiplicative Inverse of 'n'unsignedlonglongintfindMMI(unsignedlonglongintn){returnmodPow(n, MOD - 2);}// A Function to calculate the prefix hashvoidcomputePrefixHash(string str,intn,unsignedlonglongintprefix[],unsignedlonglongintpower[]){prefix[0] = 0;prefix[1] = str[0];for(inti = 2; i <= n; i++)prefix[i] = (prefix[i - 1] % MOD+ (str[i - 1] % MOD* power[i - 1] % MOD)% MOD)% MOD;return;}// A Function to calculate the suffix hash// Suffix hash is nothing but the prefix hash of// the reversed stringvoidcomputeSuffixHash(string str,intn,unsignedlonglongintsuffix[],unsignedlonglongintpower[]){suffix[0] = 0;suffix[1] = str[n - 1];for(inti = n - 2, j = 2; i >= 0 && j <= n; i--, j++)suffix[j] = (suffix[j - 1] % MOD+ (str[i] % MOD* power[j - 1] % MOD)% MOD)% MOD;return;}// A Function to answer the QueriesvoidqueryResults(string str, Query q[],intm,intn,unsignedlonglongintprefix[],unsignedlonglongintsuffix[],unsignedlonglongintpower[]){for(inti = 0; i <= m - 1; i++) {intL = q[i].L;intR = q[i].R;// Hash Value of Substring [L, R]unsignedlonglonghash_LR= ((prefix[R + 1] - prefix[L] + MOD) % MOD* findMMI(power[L]) % MOD)% MOD;// Reverse Hash Value of Substring [L, R]unsignedlonglongreverse_hash_LR= ((suffix[n - L] - suffix[n - R - 1] + MOD) % MOD* findMMI(power[n - R - 1]) % MOD)% MOD;// If both are equal then// the substring is a palindromeif(hash_LR == reverse_hash_LR) {if(isPalindrome(str, L, R) ==true)printf("The Substring [%d %d] is a ""palindrome\n",L, R);elseprintf("The Substring [%d %d] is not a ""palindrome\n",L, R);}elseprintf("The Substring [%d %d] is not a ""palindrome\n",L, R);}return;}// A Dynamic Programming Based Approach to compute the// powers of 101voidcomputePowers(unsignedlonglongintpower[],intn){// 101^0 = 1power[0] = 1;for(inti = 1; i <= n; i++)power[i] = (power[i - 1] % MOD * p % MOD) % MOD;return;}/* Driver program to test above function */intmain(){string str ="abaaabaaaba";intn = str.length();// A Table to store the powers of 101unsignedlonglongintpower[n + 1];computePowers(power, n);// Arrays to hold prefix and suffix hash valuesunsignedlonglongintprefix[n + 1], suffix[n + 1];// Compute Prefix Hash and Suffix Hash ArrayscomputePrefixHash(str, n, prefix, power);computeSuffixHash(str, n, suffix, power);Query q[] = { { 0, 10 }, { 5, 8 }, { 2, 5 }, { 5, 9 } };intm =sizeof(q) /sizeof(q[0]);queryResults(str, q, m, n, prefix, suffix, power);return(0);}chevron_rightfilter_noneJava
/* A Java program to answer queries to check whetherthe substrings are palindrome or not efficiently */publicclassGFG {staticintp =101;staticintMOD =1000000007;// Structure to represent a query. A query consists// of (L, R) and we have to answer whether the substring// from index-L to R is a palindrome or notstaticclassQuery {intL, R;publicQuery(intL,intR){this.L = L;this.R = R;}};// A function to check if a string str is palindrome// in the ranfe L to RstaticbooleanisPalindrome(String str,intL,intR){// Keep comparing characters while they are samewhile(R > L) {if(str.charAt(L++) != str.charAt(R--)) {return(false);}}return(true);}// A Function to find pow (base, exponent) % MOD// in log (exponent) timestaticintmodPow(intbase,intexponent){if(exponent ==0) {return1;}if(exponent ==1) {returnbase;}inttemp = modPow(base, exponent /2);if(exponent %2==0) {return(temp % MOD * temp % MOD) % MOD;}else{return(((temp % MOD * temp % MOD) % MOD)* base % MOD)% MOD;}}// A Function to calculate// Modulo Multiplicative Inverse of 'n'staticintfindMMI(intn){returnmodPow(n, MOD -2);}// A Function to calculate the prefix hashstaticvoidcomputePrefixHash(String str,intn,intprefix[],intpower[]){prefix[0] =0;prefix[1] = str.charAt(0);for(inti =2; i <= n; i++) {prefix[i] = (prefix[i -1] % MOD+ (str.charAt(i -1) % MOD* power[i -1] % MOD)% MOD)% MOD;}return;}// A Function to calculate the suffix hash// Suffix hash is nothing but the prefix hash of// the reversed stringstaticvoidcomputeSuffixHash(String str,intn,intsuffix[],intpower[]){suffix[0] =0;suffix[1] = str.charAt(n -1);for(inti = n -2, j =2; i >=0&& j <= n; i--, j++) {suffix[j] = (suffix[j -1] % MOD+ (str.charAt(i) % MOD* power[j -1] % MOD)% MOD)% MOD;}return;}// A Function to answer the QueriesstaticvoidqueryResults(String str, Query q[],intm,intn,intprefix[],intsuffix[],intpower[]){for(inti =0; i <= m -1; i++) {intL = q[i].L;intR = q[i].R;// Hash Value of Substring [L, R]longhash_LR= ((prefix[R +1] - prefix[L] + MOD) % MOD* findMMI(power[L]) % MOD)% MOD;// Reverse Hash Value of Substring [L, R]longreverse_hash_LR= ((suffix[n - L] - suffix[n - R -1] + MOD) % MOD* findMMI(power[n - R -1]) % MOD)% MOD;// If both are equal then the substring is a palindromeif(hash_LR == reverse_hash_LR) {if(isPalindrome(str, L, R) ==true) {System.out.printf("The Substring [%d %d] is a "+"palindrome\n",L, R);}else{System.out.printf("The Substring [%d %d] is not a "+"palindrome\n",L, R);}}else{System.out.printf("The Substring [%d %d] is not a "+"palindrome\n",L, R);}}return;}// A Dynamic Programming Based Approach to compute the// powers of 101staticvoidcomputePowers(intpower[],intn){// 101^0 = 1power[0] =1;for(inti =1; i <= n; i++) {power[i] = (power[i -1] % MOD * p % MOD) % MOD;}return;}/* Driver code */publicstaticvoidmain(String[] args){String str ="abaaabaaaba";intn = str.length();// A Table to store the powers of 101int[] power =newint[n +1];computePowers(power, n);// Arrays to hold prefix and suffix hash valuesint[] prefix =newint[n +1];int[] suffix =newint[n +1];// Compute Prefix Hash and Suffix Hash ArrayscomputePrefixHash(str, n, prefix, power);computeSuffixHash(str, n, suffix, power);Query q[] = {newQuery(0,10),newQuery(5,8),newQuery(2,5),newQuery(5,9) };intm = q.length;queryResults(str, q, m, n, prefix, suffix, power);}}// This code is contributed by Princi Singhchevron_rightfilter_noneC#
/* A C# program to answer queries to check whetherthe substrings are palindrome or not efficiently */usingSystem;classGFG {staticintp = 101;staticintMOD = 1000000007;// Structure to represent a query. A query consists// of (L, R) and we have to answer whether the substring// from index-L to R is a palindrome or notpublicclassQuery {publicintL, R;publicQuery(intL,intR){this.L = L;this.R = R;}};// A function to check if a string str is palindrome// in the ranfe L to RstaticBoolean isPalindrome(String str,intL,intR){// Keep comparing characters while they are samewhile(R > L) {if(str[L++] != str[R--]) {return(false);}}return(true);}// A Function to find pow (base, exponent) % MOD// in log (exponent) timestaticintmodPow(intBase,intexponent){if(exponent == 0) {return1;}if(exponent == 1) {returnBase;}inttemp = modPow(Base, exponent / 2);if(exponent % 2 == 0) {return(temp % MOD * temp % MOD) % MOD;}else{return(((temp % MOD * temp % MOD) % MOD) * Base % MOD) % MOD;}}// A Function to calculate Modulo Multiplicative Inverse of 'n'staticintfindMMI(intn){returnmodPow(n, MOD - 2);}// A Function to calculate the prefix hashstaticvoidcomputePrefixHash(String str,intn,int[] prefix,int[] power){prefix[0] = 0;prefix[1] = str[0];for(inti = 2; i <= n; i++) {prefix[i] = (prefix[i - 1] % MOD+ (str[i - 1] % MOD * power[i - 1] % MOD) % MOD)% MOD;}return;}// A Function to calculate the suffix hash// Suffix hash is nothing but the prefix hash of// the reversed stringstaticvoidcomputeSuffixHash(String str,intn,int[] suffix,int[] power){suffix[0] = 0;suffix[1] = str[n - 1];for(inti = n - 2, j = 2; i >= 0 && j <= n; i--, j++) {suffix[j] = (suffix[j - 1] % MOD+ (str[i] % MOD * power[j - 1] % MOD) % MOD)% MOD;}return;}// A Function to answer the QueriesstaticvoidqueryResults(String str, Query[] q,intm,intn,int[] prefix,int[] suffix,int[] power){for(inti = 0; i <= m - 1; i++) {intL = q[i].L;intR = q[i].R;// Hash Value of Substring [L, R]longhash_LR= ((prefix[R + 1] - prefix[L] + MOD) % MOD* findMMI(power[L]) % MOD)% MOD;// Reverse Hash Value of Substring [L, R]longreverse_hash_LR= ((suffix[n - L] - suffix[n - R - 1] + MOD) % MOD* findMMI(power[n - R - 1]) % MOD)% MOD;// If both are equal then the substring is a palindromeif(hash_LR == reverse_hash_LR) {if(isPalindrome(str, L, R) ==true) {Console.Write("The Substring [{0} {1}] is a "+"palindrome\n",L, R);}else{Console.Write("The Substring [{0} {1}] is not a "+"palindrome\n",L, R);}}else{Console.Write("The Substring [{0} {1}] is not a "+"palindrome\n",L, R);}}return;}// A Dynamic Programming Based Approach to compute the// powers of 101staticvoidcomputePowers(int[] power,intn){// 101^0 = 1power[0] = 1;for(inti = 1; i <= n; i++) {power[i] = (power[i - 1] % MOD * p % MOD) % MOD;}return;}/* Driver code */publicstaticvoidMain(String[] args){String str ="abaaabaaaba";intn = str.Length;// A Table to store the powers of 101int[] power =newint[n + 1];computePowers(power, n);// Arrays to hold prefix and suffix hash valuesint[] prefix =newint[n + 1];int[] suffix =newint[n + 1];// Compute Prefix Hash and Suffix Hash ArrayscomputePrefixHash(str, n, prefix, power);computeSuffixHash(str, n, suffix, power);Query[] q = {newQuery(0, 10),newQuery(5, 8),newQuery(2, 5),newQuery(5, 9) };intm = q.Length;queryResults(str, q, m, n, prefix, suffix, power);}}// This code is contributed by Rajput-Jichevron_rightfilter_noneOutput:The Substring [0 10] is a palindrome The Substring [5 8] is not a palindrome The Substring [2 5] is not a palindrome The Substring [5 9] is a palindromeThis article is contributed by Rachit Belwariar. If you like GeeksforGeeks and would like to contribute, you can also write an article and mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.
Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.
Attention reader! Don’t stop learning now. Get hold of all the important DSA concepts with the DSA Self Paced Course at a student-friendly price and become industry ready.
Recommended Posts:
- Queries on substring palindrome formation
- Queries to check if substring[L...R] is palindrome or not
- Sentence Palindrome (Palindrome after removing spaces, dots, .. etc)
- Count all palindrome which is square of a palindrome
- Length of the longest substring that do not contain any palindrome
- Count substring of Binary string such that each character belongs to a palindrome of size greater than 1
- Longest substring whose characters can be rearranged to form a Palindrome
- Length of Longest Palindrome Substring
- Check if string can be rearranged so that every Odd length Substring is Palindrome
- Find if a given string can be represented from a substring by iterating the substring ânâ times
- Partition given string in such manner that i'th substring is sum of (i-1)'th and (i-2)'th substring
- Length of the largest substring which have character with frequency greater than or equal to half of the substring
- Minimum length of substring whose rotation generates a palindromic substring
- Queries to check if the path between two nodes in a tree is a palindrome
- Count of distinct characters in a substring by given range for Q queries
- Check if a substring can be Palindromic by replacing K characters for Q queries
- Queries to check if string B exists as substring in string A
- Function to check if a singly linked list is palindrome
- Palindrome Partitioning | DP-17
- Given a number, find the next smallest palindrome

