leetcode-1177 构建回文串检测

1177. 构建回文串检测

Description

给你一个字符串 s,请你对 s 的子串进行检测。

每次检测,待检子串都可以表示为 queries[i] = [left, right, k]。我们可以 重新排列 子串 s[left], ..., s[right],并从中选择 最多 k 项替换成任何小写英文字母。

如果在上述检测过程中,子串可以变成回文形式的字符串,那么检测结果为 true,否则结果为 false

返回答案数组 answer[],其中 answer[i] 是第 i 个待检子串 queries[i] 的检测结果。

注意:在替换时,子串中的每个字母都必须作为 独立的 项进行计数,也就是说,如果 s[left..right] = "aaa"k = 2,我们只能替换其中的两个字母。(另外,任何检测都不会修改原始字符串 s,可以认为每次检测都是独立的)

示例:

1
2
3
4
5
6
7
8
输入:s = "abcda", queries = [[3,3,0],[1,2,0],[0,3,1],[0,3,2],[0,4,1]]
输出 :[true,false,false,true,true]
解释:
queries[0] : 子串 = "d",回文。
queries[1] : 子串 = "bc",不是回文。
queries[2] : 子串 = "abcd",只替换 1 个字符是变不成回文串的。
queries[3] : 子串 = "abcd",可以变成回文的 "abba"。 也可以变成 "baab",先重新排序变成 "bacd",然后把 "cd" 替换为 "ab"。
queries[4] : 子串 = "abcda",可以变成回文的 "abcba"。

提示:

  • 1 <= s.length, queries.length <= 10^5
  • 0 <= queries[i][0] <= queries[i][1] < s.length
  • 0 <= queries[i][2] <= s.length
  • s 中只有小写英文字母

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/can-make-palindrome-from-substring
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

Description

取出来的[left, right]

  • 长度为奇数,可以接受有一个落单的字符,仍为回文字符串
  • 为偶数,要为回文字符串不允许出现落单字符

题目中允许修改k个字符,如果 k >= 落单字符的数量,则字符串可以变成回文串

如果有oddNum个落单字符,至少需要修改其中的一半,当区间长度为奇数时,有一个落单字符可以不用改,考虑到奇偶,所以需要更改的字符数是(oddNum - (len % 2)) >> 1

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
public List<Boolean> canMakePaliQueries(String s, int[][] queries) {
List<Boolean> result = new ArrayList<Boolean>();

for (int[] a : queries) {
int left = a[0], right = a[1], k = a[2];
if(left == right) {
result.add(true);
continue;
}

// 对每个[left, right]范围,计算其中落单字符数量
Map<Character, Integer> countMap = new HashMap<Character, Integer>();
for (int j = left; j <= right; j++) {
countMap.put(s.charAt(j), countMap.getOrDefault(s.charAt(j), 0) + 1);
}

int oddNumber = 0;

for(Map.Entry<Character, Integer> entry : countMap.entrySet()) {
if (entry.getValue() % 2 == 1) {
oddNumber++;
}
}

int len = right - left + 1;
if (oddNumber == 0 || k >= (oddNumber - (len % 2)) >> 1) {
result.add(true);
} else {
result.add(false);
}
}

return result;
}

LeetCode题解里我觉得最简洁的做法

需要更改的字符数可以忽略区间长度带来的影响,(oddNum - (len % 2)) >> 1,oddNum减不减1对结果无影响,优化为oddNum / 2

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
39
public List<Boolean> canMakePaliQueries(String s, int[][] queries) {
int len = s.length();
List<Boolean> result = new ArrayList<Boolean>();

int cur = 0;
int[] states = new int[len];
for(int i = 0; i < len; i++) {
cur = cur ^ (1 << (s.charAt(i) - 'a' - 0)); // % 2 = 0 或 1
states[i] = cur;
}

for (int[] a : queries) {
int left = a[0], right = a[1], k = a[2];
if(left == right) {
result.add(true);
continue;
}

// 计算[left, right] 中落单字符的个数
int oddNumber = 0;
int start_state = left > 0 ? states[left-1] : 0;
int state = states[right] ^ start_state; // 相当于是减

while (state != 0) {
if ((state & 1) == 1) {
++oddNumber;
}
state = state >> 1;
}

if (oddNumber / 2 <= k) { // 不需要分奇偶
result.add(true);
} else {
result.add(false);
}
}

return result;
}