/*
Problem: Sum of Palindrome Rearrangement Costs of All Substrings
Company Tag: Intuit OA
Problem Statement:
We are given a string s.
For any substring, cost is the minimum number of character replacements
needed so that the substring can be rearranged to form a palindrome.
A string can be rearranged into a palindrome if at most one character
has odd frequency.
If oddCount is number of characters with odd frequency:
cost = oddCount / 2
Return sum of costs over all substrings.
Constraints:
1 <= s.length <= 200000
s consists of lowercase English letters.
Brute Force:
Generate every substring and count character frequencies.
Why Brute Force Fails:
There are O(n^2) substrings.
Optimized Idea:
Use prefix parity mask.
For every prefix:
bit c = 1 means character c has odd frequency till now.
For substring l to r:
odd mask = prefix[r + 1] XOR prefix[l]
Number of odd characters:
popcount(odd mask)
Explanation Block:
For every pair of prefix masks, we need:
floor(popcount(mask1 XOR mask2) / 2)
Formula:
floor(x / 2) = (x - (x % 2)) / 2
So I calculate:
1. Total Hamming distance over all prefix mask pairs.
2. Number of pairs having odd Hamming distance.
Answer:
(totalHammingDistance - oddHammingPairs) / 2
Dry Run:
s = "abc"
Substrings:
"a" -> cost 0
"b" -> cost 0
"c" -> cost 0
"ab" -> cost 1
"bc" -> cost 1
"abc" -> cost 1
Total = 3
Time Complexity:
O(26 * n)
Space Complexity:
O(n)
*/
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
long long sumPalindromeRearrangementCosts(string s) {
int n = s.size();
vector<int> prefixMasks;
prefixMasks.reserve(n + 1);
int mask = 0;
// Empty prefix mask.
prefixMasks.push_back(mask);
for (char ch : s) {
int bit = ch - 'a';
// Toggle parity of current character.
// Even frequency becomes odd.
// Odd frequency becomes even.
mask ^= (1 << bit);
prefixMasks.push_back(mask);
}
long long totalHammingDistance = 0;
// For each character bit:
// Count how many prefix masks have bit 0 and bit 1.
// Every pair with different bit value contributes 1 to Hamming distance.
for (int bit = 0; bit < 26; bit++) {
long long ones = 0;
long long zeros = 0;
for (int m : prefixMasks) {
if (m & (1 << bit)) {
ones++;
} else {
zeros++;
}
}
totalHammingDistance += ones * zeros;
}
long long evenParityMasks = 0;
long long oddParityMasks = 0;
// If two masks have different popcount parity,
// their XOR has odd Hamming distance.
for (int m : prefixMasks) {
if (__builtin_popcount((unsigned int)m) % 2 == 0) {
evenParityMasks++;
} else {
oddParityMasks++;
}
}
long long oddHammingPairs = evenParityMasks * oddParityMasks;
// Applying:
// floor(x / 2) = (x - (x % 2)) / 2
long long answer = (totalHammingDistance - oddHammingPairs) / 2;
return answer;
}
int main() {
string s = "abc";
cout << sumPalindromeRearrangementCosts(s) << endl;
return 0;
}