Given a string s 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.
Table of Content
[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.
#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;
}
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"));
}
}
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"))
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"));
}
}
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.
#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;
}
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"));
}
}
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"))
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"));
}
}
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.
#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;
}
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"));
}
}
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"))
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"));
}
}
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