0%

1177. 构建回文串检测

1177.构建回文串检测

题目描述:

给出一个字符串 $s$ ,对 $s$ 的子串进行查询。每次查询 queries[i] = [l, r, k],可以重新排列子串 s[l], ..., s[r],并且从中选择最多的 $k$ 项替换成任何小写英文字母。如果子串可以变成回文形式的字符串,那么查询结果为 true,否则结果为 false

数据范围:

$1\le s.len, q.len \le 10^5$

题解:

使用位运算加前缀和来表示前缀中各个字母个数的奇偶性。可以考虑使用异或,异或是不带进位的加减法。异或加前缀和,可以快速统计待查询的子区间中各个字母数量的奇偶性。如果一个字母为偶数,那么他可以放在子串的两边,如果为奇数,那么就需要进行替换,最中间的不需要替换。假设有 $t$ 个字母的个数为奇数,那么需要被替换的个数为 $(t - 1) / 2$ ,当 $(t - 1) / 2 \le k$ 时返回 true,否则返回 false

统计数字中 $1$ 的个数:

刚好可以使用 $x \& x - 1$ 将 $x$ 末尾的 $1$ 变成 $0$ ,因此只需要看 $x$ 能够进行几次该操作就好。

另外一个用处就是判断一个数是不是 $2^i$ ,如果 $x \& x - 1 = 0$ 则该数必是 $2 ^ i$ ,否则不是。

$x \& -x$ :

树状数组中常用的,用来得到 $x$ 的二进制中最低位的位置。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
auto optimize_cpp_stdio = []()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
class Solution
{
public:
const static int maxn = 1e5 + 1;
int sum[maxn] = {};
vector<bool> canMakePaliQueries(string& s, vector<vector<int>> &queries)
{
int n = s.size();
for (int i = 1; i <= n; ++i)
{
sum[i] = sum[i - 1] ^ (1 << (s[i - 1] - 'a'));
}
int l, r, k, tot;
vector<bool> ans(queries.size(), false);
for (int j = 0; j < queries.size(); ++j)
{
auto &q = queries[j];
l = q[0] + 1, r = q[1] + 1, k = q[2];
int cnt = 0;
tot = sum[r] ^ sum[l - 1];
while(tot)
{
tot &= tot - 1;
++cnt;
}
if (cnt <= k * 2 + 1)
ans[j] = true;
}
return ans;
}
};