[LeetCode] 1177. Can Make Palindrome from Substring

Given a string s, we make queries on substrings of s.

For each query queries[i] = [left, right, k], we may rearrange the substring s[left], ..., s[right], and then choose up to k of them to replace with any lowercase English letter. 

If the substring is possible to be a palindrome string after the operations above, the result of the query is true. Otherwise, the result is false.

Return an array answer[], where answer[i] is the result of the i-th query queries[i].

Note that: Each letter is counted individually for replacement so if for example s[left..right] = "aaa", and k = 2, we can only replace two of the letters.  (Also, note that the initial string s is never modified by any query.)

Example :

Input: s = "abcda", queries = [[3,3,0],[1,2,0],[0,3,1],[0,3,2],[0,4,1]]
Output: [true,false,false,true,true]
Explanation:
queries[0] : substring = "d", is palidrome.
queries[1] : substring = "bc", is not palidrome.
queries[2] : substring = "abcd", is not palidrome after replacing only 1 character.
queries[3] : substring = "abcd", could be changed to "abba" which is palidrome. Also this can be changed to "baab" first rearrange it "bacd" then replace "cd" with "ab".
queries[4] : substring = "abcda", could be changed to "abcba" which is palidrome.

Constraints:

  • 1 <= s.length, queries.length <= 10^5
  • 0 <= queries[i][0] <= queries[i][1] < s.length
  • 0 <= queries[i][2] <= s.length
  • s only contains lowercase English letters.

构建回文串检测。

给你一个字符串 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,可以认为每次检测都是独立的)

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

思路是前缀和 + 位运算。题意是根据每个不同的query,你截取出一个子串,判断这个子串能否在至多替换掉 K 个字母之后形成一个回文串。这个题判断回文的条件没有那么苛刻,因为在截取子串之后,题目是允许你对子串内的字母顺序进行调换的。既然是回文,那么涉及到的字母必然大多是出现偶数次,只有至多一个字母的出现次数是奇数次。这道题的暴力解是在截取了子串之后,用一个 hashset 遍历子串,在遇到重复字母的时候,把这个字母从 hashset 中 remove,这样当扫描完毕之后,hashset 内就只剩下出现次数为奇数的字母了。如果此时K能允许你替换掉hashset中一半的字母,则这个子串可以被重新组合成一个回文。举个例子,如果子串是 abcde,K = 2。这里涉及到的五个字母的出现次数其实都是一次,但是如果我们可以把de替换成ba,那么最后的结果 abcba 是可以组成一个合法的回文串的。但是由于input字符串有可能非常长,所以这个思路是会超时的。

[LeetCode] 1177. Can Make Palindrome from Substring

 

 

这里优化的方法是,我们对于需要判断的子串中不同字母的出现次数的奇偶情况,用前缀和来判断。类似560题,我们创建一个长度为 s.length() + 1 的数组来判断前 i 个字母组成的子串的二进制的前缀和。这里利用到二进制是因为对于同样一个字母,如果出现一次,那么他的二进制表达就是 1,但是出现两次的时候,我们可以通过 XOR 异或运算,使那一位又变为0同时不产生进位。举个例子,如果 i 位置上出现一个字母a,那么count[i] = 1 << s.charAt(i) - 'a'。如果 i + 1 位置上又出现一个字母a,因为是同样的字母,所以左移的次数是一样的,所以count[i + 1] = count[i] ^ (1 << s.charAt(i) - 'a')。那么二进制数字上记录 a 的那一位就又变回0了。

用这样的方式记录好input字符的二进制前缀和之后,我们对于需要求的子串,就能以O(1)的代价得到这个子串内字母的二进制表达。之后我们统计一下这个子串内有多少个1(也就是在统计这个子串内有多少个落单的字母),如果其中有一半能被K替换掉,则这个子串就可以满足题意变成回文串。

时间O(n)

空间O(n)

Java实现

 1 class Solution {
 2     public List<Boolean> canMakePaliQueries(String s, int[][] queries) {
 3         List<Boolean> res = new ArrayList<>();
 4         int[] count = new int[s.length() + 1];
 5         for (int i = 0; i < s.length(); i++) {
 6             count[i + 1] = count[i] ^ (1 << s.charAt(i) - 'a');
 7         }
 8 
 9         for (int[] q : queries) {
10             int sum = 0;
11             sum = Integer.bitCount(count[q[1] + 1] ^ count[q[0]]);
12             res.add(sum / 2 <= q[2]);
13         }
14         return res;
15     }
16 }

 

前缀和prefix sum题目总结

LeetCode 题目总结

上一篇:18:验证子串


下一篇:COFF,amd64.vc90.mfc两个布署的问题