Maximum Non-Overlapping Odd Palindrome Sum

Last Updated : 31 Jul, 2025

Given a string s consisting of lowercase English letters, find the maximum possible sum of the lengths of any two non-empty and non-overlapping palindromic substrings of odd length.
Formally, choose any two substrings s[i...j] and s[k...l] such that 1 ≤ i ≤ j < k ≤ l ≤ s.size(), both substrings are palindromes of odd length, and they do not overlap, find the maximum sum of their lengths.

Note: A palindrome is a string that reads the same forward and backward, and a substring is a contiguous sequence of characters within the string.

Examples:

Input: s = "xyabacbcz"
Output: 6
Explanation: "aba" and "cbc" are non-overlapping odd-length palindromes. Their lengths are 3 and 3 which gives the sum as 6.

Input: s = "gfgforgeeks"
Output: 4
Explanation: "gfg" and "g" are non-overlapping odd-length palindromes. Their lengths are 3 and 1 which gives the sum as 4.

Try It Yourself
redirect icon

[Naive Approach] Using Center Expansion and Prefix - O(n2) Time and O(1) Space

The idea is to find the longest odd-length palindrome ending at every position and store it in the left array. Similarly, we find the longest palindrome starting at every position and store it in the right array. Then we update both arrays so that each index carries the best possible value seen so far from its side. Finally, we try all possible split points and take the maximum sum of one palindrome from the left and one from the right, making sure they don’t overlap.

C++
#include <iostream>
#include <vector>
#include <string>
using namespace std;

int maxSum(string &s) {
    int n = s.size();
    vector<int> left(n), right(n);
    
    // find longest odd-length palindrome ending 
    // at each position
    for (int i = 0; i < n; i++) {
        int l = i, r = i;
        while (l >= 0 && r < n && s[l] == s[r]) {
            left[r] = max(left[r], 2 * (r - i + 1) - 1);
            l--; r++;
        }
    }

    // find longest odd-length palindrome starting 
    // at each position
    for (int i = n - 1; i >= 0; i--) {
        int l = i, r = i;
        while (l >= 0 && r < n && s[l] == s[r]) {
            right[l] = max(right[l], 2 * (i - l + 1) - 1);
            l--; r++;
        }
    }

    // relax left array to get max till i
    for (int i = 1; i < n; i++) {
        left[i] = max(left[i], left[i - 1]);
    }

    // relax right array to get max from i
    for (int i = n - 2; i >= 0; i--) {
        right[i] = max(right[i], right[i + 1]);
    }

    // compute the answer by combining non-overlapping palindromes
    int maxi = 0;
    for (int i = 1; i < n; i++) {
        maxi = max(maxi, left[i - 1] + right[i]);
    }

    return maxi;
}

int main() {
    string s = "xyabacbcz";
    cout << maxSum(s) << endl;
    return 0;
}
Java
class GfG {
    public static int maxSum(String s) {
        int n = s.length();
        int[] left = new int[n], right = new int[n];

        // find longest odd-length palindrome ending 
        // at each position
        for (int i = 0; i < n; i++) {
            int l = i, r = i;
            while (l >= 0 && r < n && s.charAt(l) == s.charAt(r)) {
                left[r] = Math.max(left[r], 2 * (r - i + 1) - 1);
                l--; r++;
            }
        }

        // find longest odd-length palindrome starting 
        // at each position
        for (int i = n - 1; i >= 0; i--) {
            int l = i, r = i;
            while (l >= 0 && r < n && s.charAt(l) == s.charAt(r)) {
                right[l] = Math.max(right[l], 2 * (i - l + 1) - 1);
                l--; r++;
            }
        }

        // relax left array to get max till i
        for (int i = 1; i < n; i++) {
            left[i] = Math.max(left[i], left[i - 1]);
        }

        // relax right array to get max from i
        for (int i = n - 2; i >= 0; i--) {
            right[i] = Math.max(right[i], right[i + 1]);
        }

        // compute the answer by combining non-overlapping palindromes
        int maxi = 0;
        for (int i = 1; i < n; i++) {
            maxi = Math.max(maxi, left[i - 1] + right[i]);
        }

        return maxi;
    }

    public static void main(String[] args) {
        String s = "xyabacbcz";
        System.out.println(maxSum(s));
    }
}
Python
def maxSum(s):
    n = len(s)
    left = [0] * n
    right = [0] * n

    # find longest odd-length palindrome ending 
    # at each position
    for i in range(n):
        l, r = i, i
        while l >= 0 and r < n and s[l] == s[r]:
            left[r] = max(left[r], 2 * (r - i + 1) - 1)
            l -= 1
            r += 1

    # find longest odd-length palindrome starting 
    # at each position
    for i in range(n - 1, -1, -1):
        l, r = i, i
        while l >= 0 and r < n and s[l] == s[r]:
            right[l] = max(right[l], 2 * (i - l + 1) - 1)
            l -= 1
            r += 1

    # relax left array to get max till i
    for i in range(1, n):
        left[i] = max(left[i], left[i - 1])

    # relax right array to get max from i
    for i in range(n - 2, -1, -1):
        right[i] = max(right[i], right[i + 1])

    # compute the answer by combining
    # non-overlapping palindromes
    maxi = 0
    for i in range(1, n):
        maxi = max(maxi, left[i - 1] + right[i])

    return maxi

if __name__ == "__main__":
     s = "xyabacbcz"
     print(maxSum(s))
C#
using System;

class GfG {
    public static int maxSum(string s) {
        int n = s.Length;
        int[] left = new int[n];
        int[] right = new int[n];

        // find longest odd-length palindrome ending 
        // at each position
        for (int i = 0; i < n; i++) {
            int l = i, r = i;
            while (l >= 0 && r < n && s[l] == s[r]) {
                left[r] = Math.Max(left[r], 2 * (r - i + 1) - 1);
                l--; r++;
            }
        }

        // find longest odd-length palindrome starting 
        // at each position
        for (int i = n - 1; i >= 0; i--) {
            int l = i, r = i;
            while (l >= 0 && r < n && s[l] == s[r]) {
                right[l] = Math.Max(right[l], 2 * (i - l + 1) - 1);
                l--; r++;
            }
        }

        // relax left array to get max till i
        for (int i = 1; i < n; i++) {
            left[i] = Math.Max(left[i], left[i - 1]);
        }

        // relax right array to get max from i
        for (int i = n - 2; i >= 0; i--) {
            right[i] = Math.Max(right[i], right[i + 1]);
        }

        // compute the answer by combining non-overlapping palindromes
        int maxi = 0;
        for (int i = 1; i < n; i++) {
            maxi = Math.Max(maxi, left[i - 1] + right[i]);
        }

        return maxi;
    }

    public static void Main(string[] args) {
        string s = "xyabacbcz";
        Console.WriteLine(maxSum(s));
    }
}
JavaScript
function maxSum(s) {
    const n = s.length;
    const left = Array(n).fill(0);
    const right = Array(n).fill(0);

    // find longest odd-length palindrome ending 
    // at each position
    for (let i = 0; i < n; i++) {
        let l = i, r = i;
        while (l >= 0 && r < n && s[l] === s[r]) {
            left[r] = Math.max(left[r], 2 * (r - i + 1) - 1);
            l--; r++;
        }
    }

    // find longest odd-length palindrome starting 
    // at each position
    for (let i = n - 1; i >= 0; i--) {
        let l = i, r = i;
        while (l >= 0 && r < n && s[l] === s[r]) {
            right[l] = Math.max(right[l], 2 * (i - l + 1) - 1);
            l--; r++;
        }
    }

    // relax left array to get max till i
    for (let i = 1; i < n; i++) {
        left[i] = Math.max(left[i], left[i - 1]);
    }

    // relax right array to get max from i
    for (let i = n - 2; i >= 0; i--) {
        right[i] = Math.max(right[i], right[i + 1]);
    }

    // compute the answer by combining non-overlapping palindromes
    let maxi = 0;
    for (let i = 1; i < n; i++) {
        maxi = Math.max(maxi, left[i - 1] + right[i]);
    }

    return maxi;
}

// Driver Code
const s = "xyabacbcz";
console.log(maxSum(s));

Output
6

[Better Approach] Rabin-Karp + Binary Search - O(n log n) Time and O(n) Space

The main idea is to find the two longest non-overlapping odd-length palindromic substrings, one ending on or before an index and the other starting after that index. It uses rolling hashes and binary search to efficiently check for palindromes around each center. The algorithm then combines prefix and suffix information to compute the maximum sum of their lengths.

For more details refer Rabin-Karp Algorithm for Pattern Searching

C++
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;

const int BASE = 911;
const int MOD = 1e9 + 7;

// Rolling hash class for fast
// substring hash queries
class RollingHash{
    
  private:
    vector<int> hash;
    vector<int> power;

  public:
    // Precomputes prefix hashes
    // and powers of base
    RollingHash(const string &s){
        
        int n = s.size();
        hash.resize(n + 1);
        power.resize(n + 1);
        hash[0] = 0;
        power[0] = 1;

        for (int i = 0; i < n; ++i){
            
            hash[i + 1] = ((long long)hash[i] * BASE + s[i]) % MOD;
            power[i + 1] = ((long long)power[i] * BASE) % MOD;
        }
    }

    // Returns hash of substring s[l...r]
    int getHash(int l, int r) const{
        
        int val = (hash[r + 1] - (long long)hash[l] *
                   power[r - l + 1] % MOD + MOD) % MOD;
        return val;
    }
};

// Checks if s[l...r] is a palindrome
// using forward and reverse hashes
bool isPalindrome(int l, int r, RollingHash &fHash, 
                  RollingHash &rHash, int n){
    
    int h1 = fHash.getHash(l, r);
    int h2 = rHash.getHash(n - 1 - r, n - 1 - l);
    return h1 == h2;
}

// Computes the maximum sum of lengths
// of two non-overlapping odd-length
// palindromic substrings
int maxSum(string &s){
    
    int n = s.size();
    RollingHash fHash(s);

    string rev = s;
    reverse(rev.begin(), rev.end());
    RollingHash rHash(rev);

    vector<int> leftMax(n, 1), rightMax(n, 1);

    // Compute longest odd-length palindrome
    // ending at each index
    for (int i = 0; i < n; ++i){
        
        int l = 0, r = min(i, n - 1 - i);
        int best = 1;

        // Binary search for maximum radius
        while (l <= r){
            
            int m = (l + r) / 2;
            int start = i - m;
            int end = i + m;
            if (isPalindrome(start, end, fHash, rHash, n)){
                
                best = 2 * m + 1;
                l = m + 1;
            }
            else{
                r = m - 1;
            }
        }

        int endIdx = i + best / 2;
        if (endIdx < n)
            leftMax[endIdx] = max(leftMax[endIdx], best);
    }

    // Relax leftMax array
    for (int i = n - 2; i >= 0; i--)
        leftMax[i] = max(leftMax[i], leftMax[i + 1] - 2);
    for (int i = 1; i < n; i++)
        leftMax[i] = max(leftMax[i], leftMax[i - 1]);

    // Compute longest odd-length palindrome
    // starting at each index
    for (int i = n - 1; i >= 0; --i){
        
        int l = 0, r = min(i, n - 1 - i);
        int best = 1;

        while (l <= r){
            
            int m = (l + r) / 2;
            int start = i - m;
            int end = i + m;
            if (isPalindrome(start, end, fHash, rHash, n)){
                
                best = 2 * m + 1;
                l = m + 1;
            }
            else{
                
                r = m - 1;
            }
        }

        int startIdx = i - best / 2;
        if (startIdx >= 0)
            rightMax[startIdx] = max(rightMax[startIdx], best);
    }

    // Relax rightMax array
    for (int i = 1; i < n; i++)
        rightMax[i] = max(rightMax[i], rightMax[i - 1] - 2);
    for (int i = n - 2; i >= 0; i--)
        rightMax[i] = max(rightMax[i], rightMax[i + 1]);

    // Combine the two arrays to
    // compute the maximum sum
    int ans = 0;
    for (int i = 0; i + 1 < n; ++i){
        
        ans = max(ans, leftMax[i] + rightMax[i + 1]);
    }

    return ans;
}

int main()
{
    string s = "xyabacbcz";
    cout << maxSum(s) << endl;
    return 0;
}
Java
import java.util.Arrays;

// Rolling hash class for fast 
// substring hash queries
class RollingHash {
    static final int BASE = 911;
    static final int MOD = 1000000007;

    int[] hash;
    int[] power;

    // constructor: Precomputes prefix 
    // hashes and powers of base
    public RollingHash(String s) {
        int n = s.length();
        hash = new int[n + 1];
        power = new int[n + 1];
        hash[0] = 0;
        power[0] = 1;

        for (int i = 0; i < n; i++) {
            hash[i + 1] = (int)((1L * hash[i] * BASE + s.charAt(i)) % MOD);
            power[i + 1] = (int)((1L * power[i] * BASE) % MOD);
        }
    }

    // Returns hash of substring s[l...r]
    public int getHash(int l, int r) {
        int val = (int)((hash[r + 1]
                       - 1L * hash[l] * power[r - l + 1] % MOD
                       + MOD) % MOD);
        return val;
    }
}

class GFG {

    // Checks if s[l...r] is a palindrome 
    // using forward and reverse hashes
    static boolean isPalindrome(int l, int r, 
                        RollingHash fHash, RollingHash rHash, int n) {
                            
        int h1 = fHash.getHash(l, r);
        int h2 = rHash.getHash(n - 1 - r, n - 1 - l);
        return h1 == h2;
    }

    // Computes the maximum sum of lengths 
    // of two non-overlapping odd-length 
    // palindromic substrings
    static int maxSum(String s) {
        int n = s.length();

        // Initialize rolling hash 
        // for s and its reverse
        RollingHash fHash = new RollingHash(s);
        String rev = new StringBuilder(s).reverse().toString();
        RollingHash rHash = new RollingHash(rev);

        int[] leftMax = new int[n];
        int[] rightMax = new int[n];
        Arrays.fill(leftMax, 1);
        Arrays.fill(rightMax, 1);

        // Compute longest odd-length palindrome 
        // ending at each index
        for (int i = 0; i < n; i++) {
            int lo = 0, hi = Math.min(i, n - 1 - i);
            int best = 1;

            // Binary search for maximum radius
            while (lo <= hi) {
                int m = (lo + hi) / 2;
                int start = i - m;
                int end = i + m;
                if (isPalindrome(start, end, fHash, rHash, n)) {
                    best = 2 * m + 1;
                    lo = m + 1;
                } else {
                    hi = m - 1;
                }
            }

            int endIdx = i + best / 2;
            if (endIdx < n)
                leftMax[endIdx] = Math.max(leftMax[endIdx], best);
        }

        // Relax leftMax array to 
        // cover entire left side
        for (int i = n - 2; i >= 0; i--)
            leftMax[i] = Math.max(leftMax[i], leftMax[i + 1] - 2);
        for (int i = 1; i < n; i++)
            leftMax[i] = Math.max(leftMax[i], leftMax[i - 1]);

        // Compute longest odd-length palindrome 
        // starting at each index
        for (int i = n - 1; i >= 0; i--) {
            int lo = 0, hi = Math.min(i, n - 1 - i);
            int best = 1;

            while (lo <= hi) {
                int m = (lo + hi) / 2;
                int start = i - m;
                int end = i + m;
                if (isPalindrome(start, end, fHash, rHash, n)) {
                    best = 2 * m + 1;
                    lo = m + 1;
                } else {
                    hi = m - 1;
                }
            }

            int startIdx = i - best / 2;
            if (startIdx >= 0){
                rightMax[startIdx] = 
                        Math.max(rightMax[startIdx], best);
            }
        }

        // Relax rightMax array to 
        // cover entire right side
        for (int i = 1; i < n; i++){
            rightMax[i] = 
                Math.max(rightMax[i], rightMax[i - 1] - 2);
        }
        for (int i = n - 2; i >= 0; i--)
            rightMax[i] = Math.max(rightMax[i], rightMax[i + 1]);

      
        // Combine the two arrays to 
        // compute the maximum sum
        int ans = 0;
        for (int i = 0; i + 1 < n; i++) {
            ans = Math.max(ans, leftMax[i] + rightMax[i + 1]);
        }

        return ans;
    }

    public static void main(String[] args) {
        String s = "xyabacbcz";
        System.out.println(maxSum(s));  
    }
}
Python
''' Rolling Hash Code Starts ''' 

MOD = 10**9 + 7
BASE = 911

# Precomputes prefix hashes 
# and powers of base
def RollingHash(s):
    n = len(s)
    h = [0] * (n + 1)
    p = [1] * (n + 1)
    for i in range(n):
        h[i + 1] = (h[i] * BASE + ord(s[i])) % MOD
        p[i + 1] = (p[i] * BASE) % MOD
    return h, p

# Returns hash of substring s[l...r]
def get_hash(h, p, l, r):
    val = (h[r + 1] - h[l] * p[r - l + 1]) % MOD
    return val if val >= 0 else val + MOD

# Checks if s[l...r] is a palindrome 
# using forward and reverse hashes
def isPalindrome(l, r, h1, p1, h2, p2, n):
    hash1 = get_hash(h1, p1, l, r)
    hash2 = get_hash(h2, p2, n - 1 - r, n - 1 - l)
    return hash1 == hash2
    
    
''' Rolling Hash Code Ends '''




# computes the maximum sum of lengths 
# of two non-overlapping odd-length
# palindromic substrings
def maxSum(s):
    n = len(s)
    rev = s[::-1]
    
    # compute rolling hashes for 
    # original and reversed string
    h1, p1 = RollingHash(s)
    h2, p2 = RollingHash(rev)

    leftMax = [1] * n
    rightMax = [1] * n

    # compute longest odd-length 
    # palindrome ending at each index
    for i in range(n):
        lo, hi = 0, min(i, n - 1 - i)
        best = 1
        while lo <= hi:
            m = (lo + hi) // 2
            start = i - m
            end = i + m
            if isPalindrome(start, end, h1, p1, h2, p2, n):
                best = 2 * m + 1
                lo = m + 1
            else:
                hi = m - 1
        endIdx = i + best // 2
        if endIdx < n:
            leftMax[endIdx] = max(leftMax[endIdx], best)

    # relax left_max array
    for i in range(n - 2, -1, -1):
        leftMax[i] = max(leftMax[i], leftMax[i + 1] - 2)
    for i in range(1, n):
        leftMax[i] = max(leftMax[i], leftMax[i - 1])

    # compute longest odd-length
    # palindrome starting at each index
    for i in range(n - 1, -1, -1):
        lo, hi = 0, min(i, n - 1 - i)
        best = 1
        while lo <= hi:
            m = (lo + hi) // 2
            start = i - m
            end = i + m
            if isPalindrome(start, end, h1, p1, h2, p2, n):
                best = 2 * m + 1
                lo = m + 1
            else:
                hi = m - 1
        startIdx = i - best // 2
        if startIdx >= 0:
            rightMax[startIdx] = max(rightMax[startIdx], best)

    # Relax rightMax array
    for i in range(1, n):
        rightMax[i] = max(rightMax[i], rightMax[i - 1] - 2)
    for i in range(n - 2, -1, -1):
        rightMax[i] = max(rightMax[i], rightMax[i + 1])


    # Combine the two arrays to 
    # compute the maximum sum
    ans = 0
    for i in range(n - 1):
        ans = max(ans, leftMax[i] + rightMax[i + 1])

    return ans

if __name__ == "__main__":
    s = "xyabacbcz"
    print(maxSum(s))  
C#
using System;

class RollingHash {
    
    const int BASE = 911;
    const int MOD = 1000000007;

    public int[] prefixHash;
    public int[] power;

    // Constructor: Precomputes prefix
    // hashes and powers of base
    public RollingHash(string s) {
        int n = s.Length;
        prefixHash = new int[n + 1];
        power = new int[n + 1];
        power[0] = 1;

        for (int i = 0; i < n; i++) {
            prefixHash[i + 1] = (int)((1L *
                    prefixHash[i] * BASE + s[i]) % MOD);
            power[i + 1] = (int)((1L * power[i] * BASE) % MOD);
        }
    }

    // Returns hash of substring s[l...r]
    public int GetHash(int l, int r) {
        long val = prefixHash[r + 1] - 1L * 
                prefixHash[l] * power[r - l + 1] % MOD;
        return (int)((val + MOD) % MOD);
    }
}

class GfG {

    // Checks if s[l...r] is a palindrome
    // using forward and reverse hashes
    static bool IsPalindrome(int l, int r,
        RollingHash fHash, RollingHash rHash, int n) {
        int h1 = fHash.GetHash(l, r);
        int h2 = rHash.GetHash(n - 1 - r, n - 1 - l);
        return h1 == h2;
    }

    // Computes the maximum sum of lengths 
    // of two non-overlapping odd-length 
    // palindromic substrings
    static int maxSum(string s) {
        int n = s.Length;

        // Initialize rolling hash
        // for s and its reverse
        RollingHash fHash = new RollingHash(s);
        char[] arr = s.ToCharArray();
        Array.Reverse(arr);
        string rev = new string(arr);
        RollingHash rHash = new RollingHash(rev);

        int[] leftMax = new int[n];
        int[] rightMax = new int[n];
        Array.Fill(leftMax, 1);
        Array.Fill(rightMax, 1);

        // Compute longest odd-length 
        // palindrome ending at each index
        for (int i = 0; i < n; i++) {
            int lo = 0, hi = Math.Min(i, n - 1 - i), best = 1;

            // Binary search for maximum radius
            while (lo <= hi) {
                int m = (lo + hi) / 2;
                int start = i - m;
                int end   = i + m;

                if (IsPalindrome(start, end, fHash, rHash, n)) {
                    best = 2 * m + 1;
                    lo = m + 1;
                } else {
                    hi = m - 1;
                }
            }

            int endIdx = i + best / 2;
            if (endIdx < n)
                leftMax[endIdx] = Math.Max(leftMax[endIdx], best);
        }

        // Relax leftMax array to 
        // cover entire left side
        for (int i = n - 2; i >= 0; i--)
            leftMax[i] = Math.Max(leftMax[i], leftMax[i + 1] - 2);
        for (int i = 1; i < n; i++)
            leftMax[i] = Math.Max(leftMax[i], leftMax[i - 1]);

        // Compute longest odd-length 
        // palindrome starting at each index
        for (int i = n - 1; i >= 0; i--) {
            int lo = 0, hi = Math.Min(i, n - 1 - i), best = 1;

            while (lo <= hi) {
                int m = (lo + hi) / 2;
                int start = i - m;
                int end   = i + m;

                if (IsPalindrome(start, end, fHash, rHash, n)) {
                    best = 2 * m + 1;
                    lo = m + 1;
                } else {
                    hi = m - 1;
                }
            }

            int startIdx = i - best / 2;
            if (startIdx >= 0)
                rightMax[startIdx] = Math.Max(rightMax[startIdx], best);
        }

        // Relax rightMax array to
        // cover entire right side
        for (int i = 1; i < n; i++)
            rightMax[i] = Math.Max(rightMax[i], rightMax[i - 1] - 2);
        for (int i = n - 2; i >= 0; i--)
            rightMax[i] = Math.Max(rightMax[i], rightMax[i + 1]);

        // Combine the two arrays to
        // compute the maximum sum
        int ans = 0;
        for (int i = 0; i + 1 < n; i++) {
            ans = Math.Max(ans, leftMax[i] + rightMax[i + 1]);
        }

        return ans;
    }

    static void Main(string[] args) {
        string s = "xyabacbcz";
        Console.WriteLine(maxSum(s)); 
    }
}
JavaScript
class RollingHash {
  static BASE = 911n;
  static MOD  = 1000000007n;

  constructor(s) {
    const n = s.length;
    this.hash  = Array(n + 1).fill(0n);
    this.power = Array(n + 1).fill(0n);
    this.power[0] = 1n;

    for (let i = 0; i < n; i++) {
      const code = BigInt(s.charCodeAt(i));
      // compute prefix hash and power
      this.hash[i + 1]  = 
        (this.hash[i] * RollingHash.BASE + code) % RollingHash.MOD;
      this.power[i + 1] = 
        (this.power[i] * RollingHash.BASE) % RollingHash.MOD;
    }
  }

  // returns hash of substring s[l..r]
  getHash(l, r) {
    // compute: hash[r+1] - hash[l]*BASE^(r-l+1) mod MOD
    let res = this.hash[r + 1] - (this.hash[l] * 
                        this.power[r - l + 1]) % RollingHash.MOD;
                        
    if (res < 0n) res += RollingHash.MOD;
    return res;
  }
}

// checks if substring s[l..r] is a palindrome using 
// forward and reverse hashes
function isPalindrome(l, r, fHash, rHash, n) {
  const h1 = fHash.getHash(l, r);
  const rl = n - 1 - r;
  const rr = n - 1 - l;
  const h2 = rHash.getHash(rl, rr);
  return h1 === h2;
}

// finds the maximum sum of lengths of two non-overlapping 
// odd-length palindromic substrings
function maxSum(s) {
  const n = s.length;
  const fHash = new RollingHash(s);
  const rev   = Array.from(s).reverse().join('');
  const rHash = new RollingHash(rev);

  const leftMax  = Array(n).fill(1);
  const rightMax = Array(n).fill(1);

  // compute leftMax (maximum odd-length palindrome ending 
  // at or before each position)
  for (let i = 0; i < n; i++) {
    let lo = 0, hi = Math.min(i, n - 1 - i), best = 1;
    while (lo <= hi) {
      const m = (lo + hi) >> 1;
      if (isPalindrome(i - m, i + m, fHash, rHash, n)) {
        best = 2 * m + 1;
        lo = m + 1;
      } else {
        hi = m - 1;
      }
    }
    leftMax[i + (best >> 1)] = Math.max(leftMax[i + (best >> 1)], best);
  }

  // relax leftMax to allow comparison from left to right
  for (let i = n - 2; i >= 0; i--) {
    leftMax[i] = Math.max(leftMax[i], leftMax[i + 1] - 2);
  }
  for (let i = 1; i < n; i++) {
    leftMax[i] = Math.max(leftMax[i], leftMax[i - 1]);
  }

  // compute rightMax (maximum odd-length palindrome starting
  // at or after each position)
  for (let i = n - 1; i >= 0; i--) {
    let lo = 0, hi = Math.min(i, n - 1 - i), best = 1;
    while (lo <= hi) {
      const m = (lo + hi) >> 1;
      if (isPalindrome(i - m, i + m, fHash, rHash, n)) {
        best = 2 * m + 1;
        lo = m + 1;
      } else {
        hi = m - 1;
      }
    }
    rightMax[i - (best >> 1)] = Math.max(rightMax[i - (best >> 1)], best);
  }

  // relax rightMax to allow comparison from right to left
  for (let i = 1; i < n; i++) {
    rightMax[i] = Math.max(rightMax[i], rightMax[i - 1] - 2);
  }
  for (let i = n - 2; i >= 0; i--) {
    rightMax[i] = Math.max(rightMax[i], rightMax[i + 1]);
  }

  // find the maximum total of left + right palindromes
  // for non-overlapping positions
  let ans = 0;
  for (let i = 0; i + 1 < n; i++) {
    ans = Math.max(ans, leftMax[i] + rightMax[i + 1]);
  }

  return ans;
}

// Driver Code
const s = "xyabacbcz";
console.log(maxSum(s));

Output
6

[Expected Approach] Using Manacher's Algorithm

The main idea is to use Manacher's algorithm to efficiently find the longest odd-length palindromic substring centered at every position. It builds two arrays: one tracking the longest palindrome ending at or before each index (leftMark), and another tracking the longest palindrome starting at or after each index (rightMark). By combining non-overlapping values from these arrays, it calculates the maximum sum of lengths of two disjoint odd-length palindromic substrings.

Step-by-Step Approach:

  • Transform the string using Manacher’s algorithm to handle odd-length palindromes efficiently.
  • Compute longest odd-length palindromes centered at every index using Manacher’s p[] array.
  • Build leftMark[]:
    => For each palindrome, mark its ending index with its length.
    => Propagate values backward (to allow overlap by smaller palindromes) and forward (to maintain maximum seen so far).
  • Build rightMark[]:
    => For each palindrome, mark its starting index with its length.
    => Propagate values forward and then backward, same as leftMark.
  • Find the best split:
    => Try every split between i and i+1.
    => Calculate the sum of the best palindrome ending on the left and starting on the right.
    => Keep track of the maximum sum.
  • Return the maximum sum of two non-overlapping odd-length palindromes.
C++
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

class manacher {

public:
    
    // p[i] stores radius of palindrome centered 
    // at i in the modified string
    vector<int> p;  
    string ms;  

    manacher(string &s) {
        ms = "@";
        for (char c : s) {
            ms += "#" + string(1, c);
        }
        ms += "#$";
        runManacher();
    }

    // Runs Manacher's algorithm on transformed string `ms`
    // Fills `p[i]` with radius of palindrome centered at i
    void runManacher() {
        int n = ms.size();
        p.assign(n, 0);
        int l = 0, r = 0;

        for (int i = 1; i < n - 1; ++i) {
            if (r + l - i < n && r + l - i >= 0)
                p[i] = max(0, min(r - i, p[r + l - i]));

            while (i + p[i] + 1 < n && i - p[i] - 1 >= 0
                   && ms[i + 1 + p[i]] == ms[i - 1 - p[i]]) {
                ++p[i];
            }

            if (i + p[i] > r) {
                l = i - p[i];
                r = i + p[i];
            }
        }
    }

    // Returns length of longest odd/even 
    // palindrome centered at original index
    // `cen`
    int getLongest(int cen, int odd) {
        int pos = 2 * cen + 2 + !odd;
        return p[pos];
    }

    // Utility function to check if 
    // substring s[l..r] is a palindrome
    bool check(int l, int r) {
        return (r - l + 1) <= getLongest((r + l) / 2, (r - l + 1) % 2);
    }
};


// Returns the maximum sum of lengths
// of two non-overlapping odd-length
// palindromic substrings

int maxSum(string &s) {
    int n = s.size();
    vector<int> leftMark(n, 1), rightMark(n, 1);
    manacher mr(s);

    // Fill leftMark: maximum odd-length palindrome
    // ending at or before each index
    for (int i = 0; i < n; i++) {
        int val = mr.getLongest(i, 1);
        int li = i + val / 2;
        if (li < n)
            leftMark[li] = max(leftMark[li], val);
    }

    for (int i = n - 2; i >= 0; i--) {
        leftMark[i] = max(leftMark[i], leftMark[i + 1] - 2);
    }

    for (int i = 1; i < n; i++) {
        leftMark[i] = max(leftMark[i], leftMark[i - 1]);
    }

    // Fill rightMark: maximum odd-length 
    // palindrome starting at 
    // or after each index
    for (int i = n - 1; i >= 0; i--) {
        int val = mr.getLongest(i, 1);
        int ri = i - val / 2;
        if (ri >= 0)
            rightMark[ri] = max(rightMark[ri], val);
    }

    for (int i = 1; i < n; i++) {
        rightMark[i] = max(rightMark[i], rightMark[i - 1] - 2);
    }

    for (int i = n - 2; i >= 0; i--) {
        rightMark[i] = max(rightMark[i], rightMark[i + 1]);
    }

    // Combine the two arrays to compute the 
    // maximum sum of disjoint palindromes
    int ans = 0;
    for (int i = 0; i + 1 < n; i++) {
        ans = max(ans, leftMark[i] + rightMark[i + 1]);
    }

    return ans;
}

int main() {
    string s = "xyabacbcz";
    cout << maxSum(s);
}
Java
import java.util.Arrays;

class Manacher {
    
    // p[i] stores radius of palindrome centered 
    // at i in the modified string
    int[] p;
    String ms;

    public Manacher(String s) {
        StringBuilder sb = new StringBuilder();
        sb.append("@");
        for (char c : s.toCharArray()) {
            sb.append("#").append(c);
        }
        sb.append("#$");
        ms = sb.toString();
        runManacher();
    }
    
    // Runs Manacher's algorithm on 
    // transformed string ms Fills
    // p[i] with radius of palindrome 
    // centered at i
    private void runManacher() {
        int n = ms.length();
        p = new int[n];
        int l = 0, r = 0;

        for (int i = 1; i < n - 1; i++) {
            int mirror = l + r - i;
            if (mirror >= 0 && mirror < n)
                p[i] = Math.max(0, Math.min(r - i, p[mirror]));

            while (i + p[i] + 1 < n && i - p[i] - 1 >= 0 &&
                    ms.charAt(i + 1 + p[i]) == ms.charAt(i - 1 - p[i])) {
                p[i]++;
            }

            if (i + p[i] > r) {
                l = i - p[i];
                r = i + p[i];
            }
        }
    }
    
    // Returns length of longest odd/even 
    // palindrome centered at original index
    // cen
    public int getLongest(int cen, int odd) {
        int pos = 2 * cen + 2 + (odd == 0 ? 1 : 0);
        return p[pos];
    }

    
    // Utility function to check if 
    // substring s[l..r] is a palindrome
    public boolean isPalindrome(int l, int r) {
        int len = r - l + 1;
        return len <= getLongest((l + r) / 2, len % 2);
    }
}


class GfG{
    
    // Returns the maximum sum of lengths
    // of two non-overlapping odd-length
    // palindromic substrings
    public int maxSum(String s) {
        int n = s.length();
        int[] leftMark = new int[n];
        int[] rightMark = new int[n];
        Arrays.fill(leftMark, 1);
        Arrays.fill(rightMark, 1);

        Manacher manacher = new Manacher(s);

        for (int i = 0; i < n; i++) {
            int val = manacher.getLongest(i, 1);
            int li = i + val / 2;
            if (li < n)
                leftMark[li] = Math.max(leftMark[li], val);
        }

        for (int i = n - 2; i >= 0; i--) {
            leftMark[i] = Math.max(leftMark[i], leftMark[i + 1] - 2);
        }

        for (int i = 1; i < n; i++) {
            leftMark[i] = Math.max(leftMark[i], leftMark[i - 1]);
        }

        for (int i = n - 1; i >= 0; i--) {
            int val = manacher.getLongest(i, 1);
            int ri = i - val / 2;
            if (ri >= 0)
                rightMark[ri] = Math.max(rightMark[ri], val);
        }

        for (int i = 1; i < n; i++) {
            rightMark[i] = Math.max(rightMark[i], rightMark[i - 1] - 2);
        }

        for (int i = n - 2; i >= 0; i--) {
            rightMark[i] = Math.max(rightMark[i], rightMark[i + 1]);
        }
        
        // Combine the two arrays to compute the 
        // maximum sum of disjoint palindromes
        int ans = 0;
        for (int i = 0; i + 1 < n; i++) {
            ans = Math.max(ans, leftMark[i] + rightMark[i + 1]);
        }

        return ans;
    }

    public static void main(String[] args) {
        String s = "xyabacbcz";
        GfG sol = new GfG();
        System.out.println(sol.maxSum(s));  
    }
}
Python
def manacherArray(s):
   
    # p[i] stores radius of palindrome centered 
    # at i in the modified string
    ms = '@#' + '#'.join(s) + '#$'
    n = len(ms)
    p = [0] * n
    l = r = 0

    for i in range(1, n - 1):
        mirror = l + r - i
        if 0 <= mirror < n:
            p[i] = max(0, min(r - i, p[mirror]))

        while (i + p[i] + 1 < n and i - p[i] - 1 >= 0 and
               ms[i + 1 + p[i]] == ms[i - 1 - p[i]]):
            p[i] += 1

        if i + p[i] > r:
            l = i - p[i]
            r = i + p[i]

    return p

# Returns length of longest odd/even 
# palindrome centered at original index `cen`
def getLongest(cen, odd, p):
    pos = 2 * cen + 2 + (0 if odd else 1)
    return p[pos]

# Returns the maximum sum of lengths
# of two non-overlapping odd-length
# palindromic substrings
def maxSum(s):
    n = len(s)
    leftMark = [1] * n
    rightMark = [1] * n

    p = manacherArray(s)

    # Fill leftMark: maximum odd-length palindrome
    # ending at or before each index
    for i in range(n):
        val = getLongest(i, 1, p)
        li = i + val // 2
        if li < n:
            leftMark[li] = max(leftMark[li], val)

    for i in range(n - 2, -1, -1):
        leftMark[i] = max(leftMark[i], leftMark[i + 1] - 2)

    for i in range(1, n):
        leftMark[i] = max(leftMark[i], leftMark[i - 1])

    # Fill rightMark: maximum odd-length 
    # palindrome starting at or after each index
    for i in range(n - 1, -1, -1):
        val = getLongest(i, 1, p)
        ri = i - val // 2
        if ri >= 0:
            rightMark[ri] = max(rightMark[ri], val)

    for i in range(1, n):
        rightMark[i] = max(rightMark[i], rightMark[i - 1] - 2)

    for i in range(n - 2, -1, -1):
        rightMark[i] = max(rightMark[i], rightMark[i + 1])

    # Combine the two arrays to compute the 
    # maximum sum of disjoint palindromes
    ans = 0
    for i in range(n - 1):
        ans = max(ans, leftMark[i] + rightMark[i + 1])

    return ans

if __name__ == "__main__":
    s = "xyabacbcz"
    print(maxSum(s)) 
C#
using System;
using System.Text;


class Manacher{
    
    // p[i] stores radius of palindrome centered 
    // at i in the modified string
    public int[] p;
    public string ms;

    public Manacher(string s){
        var sb = new StringBuilder();
        sb.Append("@");
        foreach (char c in s){
            sb.Append("#").Append(c);
        }
        sb.Append("#$");
        ms = sb.ToString();
        RunManacher();
    }

    // Runs Manacher's algorithm on 
    // transformed string `ms`. Fills
    // `p[i]` with radius of palindrome 
    // centered at i
    private void RunManacher(){
        int n = ms.Length;
        p = new int[n];
        int l = 0, r = 0;

        for (int i = 1; i < n - 1; i++){
            int mirror = l + r - i;
            if (mirror >= 0 && mirror < n)
                p[i] = Math.Max(0, Math.Min(r - i, p[mirror]));

            while (i + p[i] + 1 < n && i - p[i] - 1 >= 0 &&
                   ms[i + 1 + p[i]] == ms[i - 1 - p[i]]){
                p[i]++;
            }

            if (i + p[i] > r){
                l = i - p[i];
                r = i + p[i];
            }
        }
    }

    // Returns length of longest odd/even 
    // palindrome centered at original index `cen`
    public int GetLongest(int cen, int odd){
        int pos = 2 * cen + 2 + (odd == 0 ? 1 : 0);
        return p[pos];
    }

    // Utility function to check if 
    // substring s[l..r] is a palindrome
    public bool IsPalindrome(int l, int r){
        int len = r - l + 1;
        return len <= GetLongest((l + r) / 2, len % 2);
    }
}

class GfG{
    
    // Returns the maximum sum of lengths of two non-overlapping odd-length
    // palindromic substrings
    public int maxSum(string s){
        int n = s.Length;
        int[] leftMark = new int[n];
        int[] rightMark = new int[n];
        Array.Fill(leftMark, 1);
        Array.Fill(rightMark, 1);

        var manacher = new Manacher(s);

        // Fill leftMark: maximum odd-length palindrome
        // ending at or before each index
        for (int i = 0; i < n; i++){
            int val = manacher.GetLongest(i, 1);
            int li = i + val / 2;
            if (li < n)
                leftMark[li] = Math.Max(leftMark[li], val);
        }

        for (int i = n - 2; i >= 0; i--){
            leftMark[i] = Math.Max(leftMark[i], leftMark[i + 1] - 2);
        }

        for (int i = 1; i < n; i++){
            leftMark[i] = Math.Max(leftMark[i], leftMark[i - 1]);
        }

        // Fill rightMark: maximum odd-length 
        // palindrome starting at or after each index
        for (int i = n - 1; i >= 0; i--){
            int val = manacher.GetLongest(i, 1);
            int ri = i - val / 2;
            if (ri >= 0)
                rightMark[ri] = Math.Max(rightMark[ri], val);
        }

        for (int i = 1; i < n; i++){
            rightMark[i] = Math.Max(rightMark[i], rightMark[i - 1] - 2);
        }

        for (int i = n - 2; i >= 0; i--){
            rightMark[i] = Math.Max(rightMark[i], rightMark[i + 1]);
        }

        // Combine the two arrays to compute the 
        // maximum sum of disjoint palindromes
        int ans = 0;
        for (int i = 0; i + 1 < n; i++){
            ans = Math.Max(ans, leftMark[i] + rightMark[i + 1]);
        }

        return ans;
    }

    public static void Main(){
        string s = "xyabacbcz";
        GfG sol = new GfG();
        Console.WriteLine(sol.maxSum(s));  
    }
}
JavaScript
function manacherArray(s) {
 
    // p[i] stores radius of palindrome centered 
    // at i in the modified string
    const ms = '@#' + s.split('').join('#') + '#$';
    const n = ms.length;
    const p = new Array(n).fill(0);

    let l = 0, r = 0;
    for (let i = 1; i < n - 1; i++) {
        const mirror = l + r - i;
        if (mirror >= 0 && mirror < n) {
            p[i] = Math.max(0, Math.min(r - i, p[mirror]));
        }

        while (
            i + p[i] + 1 < n &&
            i - p[i] - 1 >= 0 &&
            ms[i + 1 + p[i]] === ms[i - 1 - p[i]]
        ) {
            p[i]++;
        }

        if (i + p[i] > r) {
            l = i - p[i];
            r = i + p[i];
        }
    }

    return { p, ms };
}

// Returns length of longest odd/even 
// palindrome centered at original index `cen`
function getLongest(cen, odd, p) {
    const pos = 2 * cen + 2 + (odd === 0 ? 1 : 0);
    return p[pos];
}

// Returns the maximum sum of lengths
// of two non-overlapping odd-length
// palindromic substrings
function maxSum(s) {
    const n = s.length;
    const leftMark = new Array(n).fill(1);
    const rightMark = new Array(n).fill(1);

    const { p } = manacherArray(s);

    // Fill leftMark: maximum odd-length palindrome
    // ending at or before each index
    for (let i = 0; i < n; i++) {
        const val = getLongest(i, 1, p);
        const li = i + Math.floor(val / 2);
        if (li < n) {
            leftMark[li] = Math.max(leftMark[li], val);
        }
    }

    for (let i = n - 2; i >= 0; i--) {
        leftMark[i] = Math.max(leftMark[i], leftMark[i + 1] - 2);
    }

    for (let i = 1; i < n; i++) {
        leftMark[i] = Math.max(leftMark[i], leftMark[i - 1]);
    }

    // Fill rightMark: maximum odd-length 
    // palindrome starting at or after each index
    for (let i = n - 1; i >= 0; i--) {
        const val = getLongest(i, 1, p);
        const ri = i - Math.floor(val / 2);
        if (ri >= 0) {
            rightMark[ri] = Math.max(rightMark[ri], val);
        }
    }

    for (let i = 1; i < n; i++) {
        rightMark[i] = Math.max(rightMark[i], rightMark[i - 1] - 2);
    }

    for (let i = n - 2; i >= 0; i--) {
        rightMark[i] = Math.max(rightMark[i], rightMark[i + 1]);
    }

    // Combine the two arrays to compute the 
    // maximum sum of disjoint palindromes
    let ans = 0;
    for (let i = 0; i + 1 < n; i++) {
        ans = Math.max(ans, leftMark[i] + rightMark[i + 1]);
    }

    return ans;
}


// Driver code
const s = "xyabacbcz";
console.log(maxSum(s)); 

Output
6

Time Complexity: O(n), Transforming the string, running Manacher’s algorithm, filling and relaxing the leftMark and rightMark arrays, and the final loop all take linear time. Hence, the total time complexity is O(n).
Auxiliary Space: O(n), The algorithm uses O(n) space for the transformed string, p[], leftMark[], and rightMark[]. So, the overall auxiliary space is O(n).

Comment