/*
Problem: Sum of Palindrome Rearrangement Costs of All Substrings
Company Tag: Intuit OA
Problem Statement:
We are given a string s.
For any substring of s, its 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.
For a substring:
oddCount = number of characters having odd frequency
If oddCount is 0 or 1:
cost = 0
If oddCount is greater than 1:
One replacement can fix two odd-frequency characters.
So:
cost = oddCount / 2
Return the sum of costs over all substrings.
Constraints:
1 <= s.length <= 200000
s consists of lowercase English letters.
Brute Force:
Generate every substring.
For each substring, count character frequencies and calculate cost.
Why Brute Force Fails:
There are O(n^2) substrings.
Since n can be 200000, O(n^2) is too slow.
Optimized Idea:
For every substring, I need to know how many characters have odd frequency.
I use prefix parity mask.
For every prefix:
bit c = 1 means character c has odd frequency till now.
For substring l to r:
odd frequency mask = prefix[r + 1] XOR prefix[l]
So I need:
sum of floor(popcount(prefix[i] XOR prefix[j]) / 2)
over all pairs of prefix masks.
Formula:
floor(x / 2) = (x - (x % 2)) / 2
So I calculate:
1. Total Hamming distance over all prefix mask pairs.
2. Number of pairs where Hamming distance is odd.
Answer:
(totalHammingDistance - oddHammingPairs) / 2
Dry Run:
s = "abc"
Substrings:
"a" -> oddCount = 1 -> cost = 0
"b" -> oddCount = 1 -> cost = 0
"c" -> oddCount = 1 -> cost = 0
"ab" -> oddCount = 2 -> cost = 1
"bc" -> oddCount = 2 -> cost = 1
"abc" -> oddCount = 3 -> cost = 1
Total cost = 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 the bit because frequency parity changes.
// If it was even, it becomes odd.
// If it was odd, it 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
// bit = 1
//
// Every pair with different bit values 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,
// then 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;
}