给你一个长度为 偶数 n
,下标从 0 开始的字符串 s
。
同时给你一个下标从 0 开始的二维整数数组 queries
,其中 queries[i] = [ai, bi, ci, di]
。
对于每个查询 i
,你需要执行以下操作:
- 将下标在范围
0 <= ai <= bi < n / 2
内的 子字符串s[ai:bi]
中的字符重新排列。 - 将下标在范围
n / 2 <= ci <= di < n
内的 子字符串s[ci:di]
中的字符重新排列。
对于每个查询,你的任务是判断执行操作后能否让 s
变成一个 回文串 。
每个查询与其他查询都是 独立的 。
请你返回一个下标从 0 开始的数组 answer
,如果第 i
个查询执行操作后,可以将 s
变为一个回文串,那么 answer[i] = true
,否则为 false
。
- 子字符串 指的是一个字符串中一段连续的字符序列。
s[x:y]
表示s
中从下标x
到y
且两个端点 都包含 的子字符串。
示例 1:
输入:s = "abcabc", queries = [[1,1,3,5],[0,2,5,5]] 输出:[true,true] 解释:这个例子中,有 2 个查询: 第一个查询: - a0 = 1, b0 = 1, c0 = 3, d0 = 5 - 你可以重新排列 s[1:1] => abcabc 和 s[3:5] => abcabc 。 - 为了让 s 变为回文串,s[3:5] 可以重新排列得到 => abccba 。 - 现在 s 是一个回文串。所以 answer[0] = true 。 第二个查询: - a1 = 0, b1 = 2, c1 = 5, d1 = 5. - 你可以重新排列 s[0:2] => abcabc 和 s[5:5] => abcabc 。 - 为了让 s 变为回文串,s[0:2] 可以重新排列得到 => cbaabc 。 - 现在 s 是一个回文串,所以 answer[1] = true 。
示例 2:
输入:s = "abbcdecbba", queries = [[0,2,7,9]] 输出:[false] 解释:这个示例中,只有一个查询。 a0 = 0, b0 = 2, c0 = 7, d0 = 9. 你可以重新排列 s[0:2] => abbcdecbba 和 s[7:9] => abbcdecbba 。 无法通过重新排列这些子字符串使 s 变为一个回文串,因为 s[3:6] 不是一个回文串。 所以 answer[0] = false 。
示例 3:
输入:s = "acbcab", queries = [[1,2,4,5]] 输出:[true] 解释:这个示例中,只有一个查询。 a0 = 1, b0 = 2, c0 = 4, d0 = 5. 你可以重新排列 s[1:2] => acbcab 和 s[4:5] => acbcab 。 为了让 s 变为回文串,s[1:2] 可以重新排列得到 => abccab。 然后 s[4:5] 重新排列得到 abccba 。 现在 s 是一个回文串,所以 answer[0] = true 。
提示:
2 <= n == s.length <= 10^5
1 <= queries.length <= 10^5
queries[i].length == 4
ai == queries[i][0], bi == queries[i][1]
ci == queries[i][2], di == queries[i][3]
0 <= ai <= bi < n / 2
n / 2 <= ci <= di < n
n
是一个偶数。s
只包含小写英文字母。
提示 1
Consider two indices, x
on the left side and its symmetrical index y
on the right side.
提示 2
Store the frequencies of all of the letters in both intervals [ai, bi]
and [ci, di]
in a query.
提示 3
If x
is not in [ai, bi]
and y
is not in [ci, di]
, they must be the same.
提示 4
If x
is in [ai, bi]
and y
is not in [ci, di]
, remove one occurrence of the character at index y
from the frequency array on the left side.
提示 5
Similarly, if x
is not in [ai, bi]
and y
is in [ci, di]
, remove one occurrence of the character at index x
from the frequency array on the right side.
提示 6
Finally, check whether the two frequency arrays are the same, and the indices that don't fall into any of the intervals are the same as well.
提示 7
Use prefix-sum + hashing to improve the time complexity.
解法:前缀和 + 哈希表 / 频率数组
提示 1
考虑两个下标,左边是 x,右边是其对称下标 y。
提示 2
在查询中存储 [ai, bi] 和 [ci, di] 两个区间中所有字母的频率。
提示 3
如果 x 不在 [ai,bi] 中,而 y 不在 [ci,di] 中,那么它们必须是相同的。
提示 4
如果 x 在 [ai,bi] 中,而 y 不在 [ci,di]中,则从左侧的频率数组中删除索引为 y 的字符的一次出现。
提示 5
同样,如果 x 不在 [ai,bi] 中,而 y 在 [ci,di]中,则从右侧的频率数组中删除索引 x 处的一个字符。
提示 6
最后,检查两个频率数组是否相同,以及不属于任何区间的索引是否也相同。
提示 7
使用前缀和 + 哈希算法来提高时间复杂度。
Java版:
频率数组写法:
class Solution {
public boolean[] canMakePalindromeQueries(String s, int[][] queries) {
int n = s.length();
int[][] counts = new int[n + 1][26];
for (int i = 0; i < n; i++) {
System.arraycopy(counts[i], 0, counts[i + 1], 0, 26);
counts[i + 1][s.charAt(i) - 'a']++;
}
int m = queries.length;
boolean[] ans = new boolean[m];
for (int i = 0; i < m; i++) {
if (i > 0 && Arrays.equals(queries[i], queries[i - 1])) {
ans[i] = ans[i - 1];
continue;
}
int[] leftmap = new int[26];
int[] rightmap = new int[26];
int a = queries[i][0];
int b = queries[i][1];
int c = queries[i][2];
int d = queries[i][3];
for (int j = 0; j < 26; j++) {
leftmap[j] = counts[b + 1][j] - counts[a][j];
rightmap[j] = counts[d + 1][j] - counts[c][j];
}
int x = 0;
int y = n - 1;
boolean check = true;
while (x < y) {
if ((x < a || x > b) && (y < c || y > d)) {
if (s.charAt(x) != s.charAt(y)) {
check = false;
break;
}
} else if ( (x >= a && x <= b) && (y < c || y > d) ) {
int del = s.charAt(y) - 'a';
if (leftmap[del] > 0) {
leftmap[del]--;
} else {
check = false;
break;
}
} else if ( (x < a || x > b) && (y >= c && y <= d)) {
int del = s.charAt(x) - 'a';
if (rightmap[del] > 0) {
rightmap[del]--;
} else {
check = false;
break;
}
}
x++;
y--;
}
if (check == false) {
ans[i] = false;
continue;
}
if (Arrays.equals(leftmap, rightmap)) {
ans[i] = true;
}
}
return ans;
}
}
哈希表写法:
class Solution {
public boolean[] canMakePalindromeQueries(String s, int[][] queries) {
int n = s.length();
int[][] counts = new int[n + 1][26];
for (int i = 0; i < n; i++) {
System.arraycopy(counts[i], 0, counts[i + 1], 0, 26);
counts[i + 1][s.charAt(i) - 'a']++;
}
int m = queries.length;
boolean[] ans = new boolean[m];
for (int i = 0; i < m; i++) {
if (i > 0 && Arrays.equals(queries[i], queries[i - 1])) {
ans[i] = ans[i - 1];
continue;
}
Map<Integer, Integer> leftmap = new HashMap<>();
Map<Integer, Integer> rightmap = new HashMap<>();
int a = queries[i][0];
int b = queries[i][1];
int c = queries[i][2];
int d = queries[i][3];
for (int j = 0; j < 26; j++) {
if (counts[b + 1][j] - counts[a][j] > 0) {
leftmap.put(j, counts[b + 1][j] - counts[a][j]);
}
if (counts[d + 1][j] - counts[c][j] > 0) {
rightmap.put(j, counts[d + 1][j] - counts[c][j]);
}
}
int x = 0;
int y = n - 1;
boolean check = true;
while (x < y) {
if ((x < a || x > b) && (y < c || y > d)) {
if (s.charAt(x) != s.charAt(y)) {
check = false;
break;
}
} else if ( (x >= a && x <= b) && (y < c || y > d) ) {
int del = s.charAt(y) - 'a';
if (leftmap.containsKey(del)) {
if (leftmap.merge(del, -1, Integer::sum) == 0) {
leftmap.remove(del);
}
} else {
check = false;
break;
}
} else if ( (x < a || x > b) && (y >= c && y <= d)) {
int del = s.charAt(x) - 'a';
if (rightmap.containsKey(del)) {
if (rightmap.merge(del, -1, Integer::sum) == 0) {
rightmap.remove(del);
}
} else {
check = false;
break;
}
}
x++;
y--;
}
if (check == false) {
ans[i] = false;
continue;
}
if (leftmap.equals(rightmap)) {
ans[i] = true;
}
}
return ans;
}
}
Python3版:
class Solution:
def canMakePalindromeQueries(self, s: str, queries: List[List[int]]) -> List[bool]:
n = len(s)
counts = [[0] * 26 for _ in range(n + 1)]
for i, c in enumerate(s):
counts[i + 1] = counts[i].copy()
counts[i + 1][ord(c) - ord('a')] += 1
ans = [False] * len(queries)
for i, query in enumerate(queries):
if i > 0 and queries[i] == queries[i - 1]:
ans[i] = ans[i - 1]
continue
a, b, c, d = query[0], query[1], query[2], query[3]
leftmap = [0] * 26
rightmap = [0] * 26
for j in range(26):
leftmap[j] = counts[b + 1][j] - counts[a][j]
rightmap[j] = counts[d + 1][j] - counts[c][j]
x = 0
y = n - 1
check = True
while x < y:
if (x < a or x > b) and (y < c or y > d):
if s[x] != s[y]:
check = False
break
elif (x >= a and x <= b) and (y < c or y > d):
delete = ord(s[y]) - ord('a')
if leftmap[delete] > 0:
leftmap[delete] -= 1
else:
check = False
break
elif (x < a or x > b) and (y >= c and y <= d):
delete = ord(s[x]) - ord('a')
if rightmap[delete] > 0:
rightmap[delete] -= 1
else:
check = False
break
x += 1
y -= 1
if not check:
ans[i] = False
continue
if leftmap == rightmap:
ans[i] = True
return ans
复杂度分析
- 时间复杂度:O(nA + m(n + A) ),其中 n 为 s 的长度,m 为 queries 的长度,A 为字符集合的大小,本题中字符均为小写字母,所以 A=26。回答每个询问的时间是 O(A)。
- 空间复杂度:O(nA + m)。