Longest Substring to Form a Palindrome

Last Updated : 20 Jul, 2026

Given a string which only contains lowercase alphabets. Find the length of the longest substring of s such that the characters in it can be rearranged to form a palindrome.

Examples:

Input: s = "aabe"
Output: 3
Explanation: The substring "aab" can be rearranged to "aba" which is the longest palindrome possible for this String.

Input: s = "adbabd"
Output: 6
Explanation: The whole string "adbabd" can be rearranged to form a palindromic substring. One possible arrangement is "abddba". Thus, output length of the string is 6.

Try It Yourself
redirect icon

[Naive Approach] Check All Substrings - O(|s| ^ 3 * 26) Time and O(26) Space

Generate every possible substring and count the frequency of each character in that substring. A substring can be rearranged into a palindrome if at most one character has an odd frequency. For every substring, check this condition and update the maximum length whenever a valid substring is found.

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

bool canFormPalindrome(string& s, int l, int r) {
    vector<int> freq(26, 0);

    for (int i = l; i <= r; i++) {
        freq[s[i] - 'a']++;
    }

    int odd = 0;
    for (int x : freq) {
        if (x % 2) odd++;
    }

    return odd <= 1;
}

int longestSubstring(string& s) {
    int n = s.length();
    int ans = 0;

    for (int i = 0; i < n; i++) {
        for (int j = i; j < n; j++) {
            if (canFormPalindrome(s, i, j)) {
                ans = max(ans, j - i + 1);
            }
        }
    }

    return ans;
}

int main() {
    cout << longestSubstring("aabe") << endl;
    cout << longestSubstring("adbabd") << endl;
    return 0;
}
Java
class GFG {
    static boolean canFormPalindrome(String s, int l, int r) {
        int[] freq = new int[26];

        for (int i = l; i <= r; i++) {
            freq[s.charAt(i) - 'a']++;
        }

        int odd = 0;
        for (int x : freq) {
            if (x % 2 == 1) odd++;
        }

        return odd <= 1;
    }

    static int longestSubstring(String s) {
        int n = s.length();
        int ans = 0;

        for (int i = 0; i < n; i++) {
            for (int j = i; j < n; j++) {
                if (canFormPalindrome(s, i, j)) {
                    ans = Math.max(ans, j - i + 1);
                }
            }
        }

        return ans;
    }

    public static void main(String[] args) {
        System.out.println(longestSubstring("aabe"));
        System.out.println(longestSubstring("adbabd"));
    }
}
Python
def canFormPalindrome(s, l, r):
    freq = [0] * 26

    for i in range(l, r + 1):
        freq[ord(s[i]) - ord('a')] += 1

    odd = 0
    for x in freq:
        if x % 2:
            odd += 1

    return odd <= 1


def longestSubstring(s):
    n = len(s)
    ans = 0

    for i in range(n):
        for j in range(i, n):
            if canFormPalindrome(s, i, j):
                ans = max(ans, j - i + 1)

    return ans

if __name__ == "__main__":
    print(longestSubstring("aabe"))
    print(longestSubstring("adbabd"))
C#
using System;

class GFG {
    static bool CanFormPalindrome(string s, int l, int r) {
        int[] freq = new int[26];

        for (int i = l; i <= r; i++) {
            freq[s[i] - 'a']++;
        }

        int odd = 0;
        foreach (int x in freq) {
            if (x % 2 == 1) odd++;
        }

        return odd <= 1;
    }

    static int longestSubstring(string s) {
        int n = s.Length;
        int ans = 0;

        for (int i = 0; i < n; i++) {
            for (int j = i; j < n; j++) {
                if (CanFormPalindrome(s, i, j)) {
                    ans = Math.Max(ans, j - i + 1);
                }
            }
        }

        return ans;
    }

    static void Main() {
        Console.WriteLine(longestSubstring("aabe"));
        Console.WriteLine(longestSubstring("adbabd"));
    }
}
JavaScript
function canFormPalindrome(s, l, r) {
    let freq = new Array(26).fill(0);

    for (let i = l; i <= r; i++) {
        freq[s.charCodeAt(i) - 97]++;
    }

    let odd = 0;
    for (let x of freq) {
        if (x % 2 === 1) odd++;
    }

    return odd <= 1;
}

function longestSubstring(s) {
    let n = s.length;
    let ans = 0;

    for (let i = 0; i < n; i++) {
        for (let j = i; j < n; j++) {
            if (canFormPalindrome(s, i, j)) {
                ans = Math.max(ans, j - i + 1);
            }
        }
    }

    return ans;
}

// Driver Code
console.log(longestSubstring("aabe"));
console.log(longestSubstring("adbabd"));

Output
3
6

[Better Approach] Prefix Mask and Check All Pairs - O(|s| ^ 2) Time and O(|s|) Space

The idea is to maintain the parity (odd/even occurrence) of characters using a bitmask prefix array. For any substring, its parity mask can be obtained using XOR of two prefix masks. A substring can be rearranged into a palindrome if its mask has either no set bits (all frequencies even) or exactly one set bit (one odd frequency), which can be checked efficiently using bit operations.

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

int longestSubstring(string& s) {
    int n = s.length();
    vector<int> prefix(n + 1, 0);

    for (int i = 0; i < n; i++) {
        prefix[i + 1] = prefix[i] ^ (1 << (s[i] - 'a'));
    }

    int ans = 0;

    for (int i = 0; i < n; i++) {
        for (int j = i; j < n; j++) {
            int mask = prefix[j + 1] ^ prefix[i];

            // Valid if at most one character has odd frequency.
            if ((mask & (mask - 1)) == 0) {
                ans = max(ans, j - i + 1);
            }
        }
    }

    return ans;
}

int main() {
    cout << longestSubstring("aabe") << endl;
    cout << longestSubstring("adbabd") << endl;
    return 0;
}
Java
class GFG {
    static int longestSubstring(String s) {
        int n = s.length();
        int[] prefix = new int[n + 1];

        for (int i = 0; i < n; i++) {
            prefix[i + 1] = prefix[i] ^ (1 << (s.charAt(i) - 'a'));
        }

        int ans = 0;

        for (int i = 0; i < n; i++) {
            for (int j = i; j < n; j++) {
                int mask = prefix[j + 1] ^ prefix[i];

                // Valid if at most one character has odd frequency.
                if ((mask & (mask - 1)) == 0) {
                    ans = Math.max(ans, j - i + 1);
                }
            }
        }

        return ans;
    }

    public static void main(String[] args) {
        System.out.println(longestSubstring("aabe"));
        System.out.println(longestSubstring("adbabd"));
    }
}
Python
def longestSubstring(s):
    n = len(s)
    prefix = [0] * (n + 1)

    for i in range(n):
        prefix[i + 1] = prefix[i] ^ (1 << (ord(s[i]) - ord('a')))

    ans = 0

    for i in range(n):
        for j in range(i, n):
            mask = prefix[j + 1] ^ prefix[i]

            # Valid if at most one character has odd frequency.
            if mask & (mask - 1) == 0:
                ans = max(ans, j - i + 1)

    return ans

if __name__ == "__main__":
    print(longestSubstring("aabe"))
    print(longestSubstring("adbabd"))
C#
using System;

class GFG {
    static int longestSubstring(string s) {
        int n = s.Length;
        int[] prefix = new int[n + 1];

        for (int i = 0; i < n; i++) {
            prefix[i + 1] = prefix[i] ^ (1 << (s[i] - 'a'));
        }

        int ans = 0;

        for (int i = 0; i < n; i++) {
            for (int j = i; j < n; j++) {
                int mask = prefix[j + 1] ^ prefix[i];

                // Valid if at most one character has odd frequency.
                if ((mask & (mask - 1)) == 0) {
                    ans = Math.Max(ans, j - i + 1);
                }
            }
        }

        return ans;
    }

    static void Main() {
        Console.WriteLine(longestSubstring("aabe"));
        Console.WriteLine(longestSubstring("adbabd"));
    }
}
JavaScript
function longestSubstring(s) {
    let n = s.length;
    let prefix = new Array(n + 1).fill(0);

    for (let i = 0; i < n; i++) {
        prefix[i + 1] = prefix[i] ^ (1 << (s.charCodeAt(i) - 97));
    }

    let ans = 0;

    for (let i = 0; i < n; i++) {
        for (let j = i; j < n; j++) {
            let mask = prefix[j + 1] ^ prefix[i];

            // Valid if at most one character has odd frequency.
            if ((mask & (mask - 1)) === 0) {
                ans = Math.max(ans, j - i + 1);
            }
        }
    }

    return ans;
}

// Driver Code
console.log(longestSubstring("aabe"));
console.log(longestSubstring("adbabd"));

Output
3
6

[Expected Approach] Prefix Mask with First Occurrence - O(|s| * 26) Time and O(|s| * 26) Space

Maintain a bitmask representing the parity of character frequencies seen so far and store the first occurrence of every mask. If the same mask appears again, all characters between those positions have even frequencies. To allow one odd-frequency character, try toggling each of the 26 bits and check whether the resulting mask appeared earlier. This avoids checking all substrings and directly finds the longest valid substring ending at each position, reducing the complexity to O(|s| × 26).

Key Observation

A string can be rearranged into a palindrome if below follow any one condition:

  • All characters occur an even number of times.
  • Exactly one character occurs an odd number of times.

Why Bitmask Works?

Each bit represents whether the frequency of a character is odd or even.

  • Bit = 0 -> Even frequency
  • Bit = 1 -> Odd frequency

For Example:

  • mask = 000...000 (All frequencies are even.)
  • mask = 000...1000 (Only one character has odd frequency.)
  • Both cases are valid for forming a palindrome.
C++
#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;

int longestSubstring(string& s) {
    unordered_map<int, int> index;

    int ans = 0;
    int mask = 0;

    index[0] = -1;

    for (int i = 0; i < s.length(); i++) {
        
        // Toggle parity of current character.
        mask ^= (1 << (s[i] - 'a'));

        if (index.find(mask) != index.end()) {
            ans = max(ans, i - index[mask]);
        } else {
            index[mask] = i;
        }

        // Check masks differing by one bit.
        for (int j = 0; j < 26; j++) {
            int newMask = mask ^ (1 << j);

            if (index.find(newMask) != index.end()) {
                ans = max(ans, i - index[newMask]);
            }
        }
    }

    return ans;
}

int main() {
    cout << longestSubstring("aabe") << endl;
    cout << longestSubstring("adbabd") << endl;
    return 0;
}
Java
import java.util.HashMap;

class GFG {
    static int longestSubstring(String s) {
        HashMap<Integer, Integer> index = new HashMap<>();

        int ans = 0;
        int mask = 0;

        index.put(0, -1);

        for (int i = 0; i < s.length(); i++) {
            
            // Toggle parity of current character.
            mask ^= (1 << (s.charAt(i) - 'a'));

            if (index.containsKey(mask)) {
                ans = Math.max(ans, i - index.get(mask));
            } else {
                index.put(mask, i);
            }

            // Check masks differing by one bit.
            for (int j = 0; j < 26; j++) {
                int newMask = mask ^ (1 << j);

                if (index.containsKey(newMask)) {
                    ans = Math.max(ans, i - index.get(newMask));
                }
            }
        }

        return ans;
    }

    public static void main(String[] args) {
        System.out.println(longestSubstring("aabe"));
        System.out.println(longestSubstring("adbabd"));
    }
}
Python
def longestSubstring(s):
    index = {0: -1}

    ans = 0
    mask = 0

    for i, ch in enumerate(s):
        
        # Toggle parity of current character.
        mask ^= (1 << (ord(ch) - ord('a')))

        if mask in index:
            ans = max(ans, i - index[mask])
        else:
            index[mask] = i

        # Check masks differing by one bit.
        for j in range(26):
            newMask = mask ^ (1 << j)

            if newMask in index:
                ans = max(ans, i - index[newMask])

    return ans

if __name__ == "__main__":
    print(longestSubstring("aabe"))
    print(longestSubstring("adbabd"))
C#
using System;
using System.Collections.Generic;

class GFG {
    static int longestSubstring(string s) {
        Dictionary<int, int> index = new Dictionary<int, int>();

        int ans = 0;
        int mask = 0;

        index[0] = -1;

        for (int i = 0; i < s.Length; i++) {
            
            // Toggle parity of current character.
            mask ^= (1 << (s[i] - 'a'));

            if (index.ContainsKey(mask)) {
                ans = Math.Max(ans, i - index[mask]);
            } else {
                index[mask] = i;
            }

            // Check masks differing by one bit.
            for (int j = 0; j < 26; j++) {
                int newMask = mask ^ (1 << j);

                if (index.ContainsKey(newMask)) {
                    ans = Math.Max(ans, i - index[newMask]);
                }
            }
        }

        return ans;
    }

    static void Main() {
        Console.WriteLine(longestSubstring("aabe"));
        Console.WriteLine(longestSubstring("adbabd"));
    }
}
JavaScript
function longestSubstring(s) {
    let index = new Map();

    let ans = 0;
    let mask = 0;

    index.set(0, -1);

    for (let i = 0; i < s.length; i++) {
        
        // Toggle parity of current character.
        mask ^= (1 << (s.charCodeAt(i) - 97));

        if (index.has(mask)) {
            ans = Math.max(ans, i - index.get(mask));
        } else {
            index.set(mask, i);
        }

        // Check masks differing by one bit.
        for (let j = 0; j < 26; j++) {
            let newMask = mask ^ (1 << j);

            if (index.has(newMask)) {
                ans = Math.max(ans, i - index.get(newMask));
            }
        }
    }

    return ans;
}

// Driver Code
console.log(longestSubstring("aabe"));
console.log(longestSubstring("adbabd"));

Output
3
6
Comment