Hard
Palindrome Rearrangement Queries — C++
Full explanation · Time O(26 + d * n + d * q) · Space O(26 + d * n)
// Time: O(26 + d * n + d * q)
// Space: O(26 + d * n)
// prefix sum, freq table
class Solution {
public:
vector<bool> canMakePalindromeQueries(string s, vector<vector<int>>& queries) {
vector<int> lookup(26, -1);
int d = 0;
for (const auto& c : s) {
if (lookup[c - 'a'] != -1) {
continue;
}
lookup[c - 'a'] = d++;
}
vector<int> prefix(size(s) / 2 + 1);
vector<vector<int>> prefixs1(size(s) / 2 + 1, vector<int>(d));
vector<vector<int>> prefixs2(size(s) / 2 + 1, vector<int>(d));
for (int i = 0; i < size(s) / 2; ++i) {
const int x = lookup[s[i] - 'a'], y = lookup[s[(size(s) - 1) - i] - 'a'];
prefix[i + 1] = prefix[i] + (x != y ? 1 : 0);
for (int j = 0; j < d; ++j) {
prefixs1[i + 1][j] = prefixs1[i][j] + (j == x ? 1 : 0);
prefixs2[i + 1][j] = prefixs2[i][j] + (j == y ? 1 : 0);
}
}
const auto& check = [&](int left1, int right1, int left2, int right2) {
const auto& same = [&](int left, int right) {
for (int i = 0; i < d; ++i) {
if ((prefixs1[right + 1][i] - prefixs1[left][i]) - (prefixs2[right + 1][i] - prefixs1[left][i]) != 0) {
return false;
}
}
return true;
};
const auto& min_left = min(left1, left2), &max_left = max(left1, left2);
const auto& min_right = min(right1, right2), &max_right = max(right1, right2);
if (!(prefix[min_left] - prefix[0] == 0 && prefix.back() - prefix[max_right + 1] == 0)) {
return false;
}
if (min_right < max_left) { // non-overlapped
return prefix[max_left] - prefix[min_right + 1] == 0 && same(min_left, min_right) && same(max_left, max_right);
}
// overlapped
if ((left1 == min_left) == (right1 == max_right)) { // inside another
return same(min_left, max_right);
}
// not inside another
auto p1 = &prefixs1, p2 = &prefixs2;
if (min_left != left1) {
swap(p1, p2);
}
for (int i = 0; i < d; ++i) {
const int d1 = ((*p1)[min_right + 1][i] - (*p1)[min_left][i]) - ((*p2)[max_left][i] - (*p2)[min_left][i]);
const int d2 = ((*p2)[max_right + 1][i] - (*p2)[max_left][i]) - ((*p1)[max_right + 1][i] - (*p1)[min_right + 1][i]);
if (!(d1 == d2 && d1 >= 0)) { // test case: s = "aabbba", queries = [[0,1,3,4]]
return false;
}
}
return true;
};
vector<bool> result(size(queries));
for (int i = 0; i < size(queries); ++i) {
result[i] = check(queries[i][0], queries[i][1], (size(s) - 1) - queries[i][3], (size(s) - 1) - queries[i][2]);
}
return result;
}
};
// Time: O(26 * n + 26 * q)
// Space: O(26 * n)
// prefix sum, freq table
class Solution2 {
public:
vector<bool> canMakePalindromeQueries(string s, vector<vector<int>>& queries) {
vector<int> prefix(size(s) / 2 + 1);
vector<vector<int>> prefixs1(size(s) / 2 + 1, vector<int>(26));
vector<vector<int>> prefixs2(size(s) / 2 + 1, vector<int>(26));
for (int i = 0; i < size(s) / 2; ++i) {
const int x = s[i] - 'a', y = s[(size(s) - 1) - i] - 'a';
prefix[i + 1] = prefix[i] + (x != y ? 1 : 0);
for (int j = 0; j < 26; ++j) {
prefixs1[i + 1][j] = prefixs1[i][j] + (j == x ? 1 : 0);
prefixs2[i + 1][j] = prefixs2[i][j] + (j == y ? 1 : 0);
}
}
const auto& check = [&](int left1, int right1, int left2, int right2) {
const auto& same = [&](int left, int right) {
for (int i = 0; i < 26; ++i) {
if ((prefixs1[right + 1][i] - prefixs1[left][i]) - (prefixs2[right + 1][i] - prefixs1[left][i]) != 0) {
return false;
}
}
return true;
};
const auto& min_left = min(left1, left2), &max_left = max(left1, left2);
const auto& min_right = min(right1, right2), &max_right = max(right1, right2);
if (!(prefix[min_left] - prefix[0] == 0 && prefix.back() - prefix[max_right + 1] == 0)) {
return false;
}
if (min_right < max_left) { // non-overlapped
return prefix[max_left] - prefix[min_right + 1] == 0 && same(min_left, min_right) && same(max_left, max_right);
}
// overlapped
if ((left1 == min_left) == (right1 == max_right)) { // inside another
return same(min_left, max_right);
}
// not inside another
auto p1 = &prefixs1, p2 = &prefixs2;
if (min_left != left1) {
swap(p1, p2);
}
for (int i = 0; i < 26; ++i) {
const int d1 = ((*p1)[min_right + 1][i] - (*p1)[min_left][i]) - ((*p2)[max_left][i] - (*p2)[min_left][i]);
const int d2 = ((*p2)[max_right + 1][i] - (*p2)[max_left][i]) - ((*p1)[max_right + 1][i] - (*p1)[min_right + 1][i]);
if (!(d1 == d2 && d1 >= 0)) { // test case: s = "aabbba", queries = [[0,1,3,4]]
return false;
}
}
return true;
};
vector<bool> result(size(queries));
for (int i = 0; i < size(queries); ++i) {
result[i] = check(queries[i][0], queries[i][1], (size(s) - 1) - queries[i][3], (size(s) - 1) - queries[i][2]);
}
return result;
}
};