1177. Can Make Palindrome from Substring

问题:

给定一个字符串s,

和一个字串操作数组queries

[i, j, k]

即对字符串s的i~j字符组成的子串,进行重新排列,且可从中最多(up to)选取k个字母,替换成任意字母,

使得子串能够成为回文字符串。

如果可以返回true,否则返回false。

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.

  

解法:

回文字符串:

特点:中间对称,前后两元素两两相同。

由于本问题中,子字符串可以进行重新排序,因此对子字符串的顺序就不需要考虑了。

只计算子字符串中,出现的字母个数,是否两两可相消。

最后不能相互抵消的字母(假如有n个)中一半(n/2),替换成已存在的字母,形成新的相消对,即可成为回文字符串。

最多需要n/2次字母替换,n/2<=k,则满足题意,可返回true。

 

两两相消:

这里我们想到了异或^运算:两两不同=1,两两相同=0

使用bit位法,标记字母a~z:1<<(A[i]-'a')

求字母出现的个数是否两两相消:

求前缀字符串不能相消的字母标记:prefix ^= 1<<(A[i]-'a')

i~j的子字符串:

我们想到前缀求和的算法:substring(i,j)=presum(j)-presum(i-1)

ps->presum的简写:

ps(0)=0;
ps(1)=sum(0,0);
ps(2)=sum(0,1);
ps(3)=sum(0,3);

因此:sum(i,j)=ps(j+1)-ps(i)

本题,在字母计数中,前缀求和即为:子字符串i~j,计算不能抵消的字母个数

sum(i,j) = ps(j+1) ^ ps(i)

抵消相同前缀。

得到的sum(i,j)即为子字符串中出现奇数次(不能相消)的字母标记,

要求有多少个这样的字母,即求这个标记中有多少个 1 即可。

__builtin_popcount:该函数求参数变量中有多少个 1

/2与k比较即可。

 

代码参考:

 1 class Solution {
 2 public:
 3     vector<bool> canMakePaliQueries(string s, vector<vector<int>>& queries) {
 4         vector<bool> res;
 5         int prefix=0;
 6         vector<int> ps(1);
 7         //保存前缀字符串0~j=prefix
 8         //presum(0)=0, sum(i~j)=presum(j)-presum(i)
 9         for(char c:s){
10             ps.push_back(prefix ^= 1<<(c-'a'));//去掉偶数个相同的字母
11         }
12         for(vector<int>q : queries){
13             int odds = __builtin_popcount(ps[q[1]+1]^ps[q[0]]);
14             //用异或^去掉:相同的部分前缀0~i-1
15             //字符串sum(i~j)中,奇数个字母的个数
16             res.push_back(odds/2<=q[2]);
17         }
18         return res;
19     }
20 };

 

posted @ 2020-06-13 12:19  habibah_chang  阅读(191)  评论(0编辑  收藏  举报