题目列表
2980. 检查按位或是否存在尾随零
2981. 找出出现至少三次的最长特殊子字符串 I
2982. 找出出现至少三次的最长特殊子字符串 II
2983. 回文串重新排列查询
一、检查按位或是否存在尾随零
这题和位运算有关,不是很难,题目要求至少有两个数的或运算存在一个尾随零,那么我们只管检查每个数字的二进制的最低位是否为0即可,即判断数字的奇偶性(奇数的二进制最低位为1,偶数的二进制最低位为0)
代码如下
class Solution {
public:bool hasTrailingZeros(vector<int>& nums) {int cnt=0;for(auto&e:nums){if((e&1)==0){cnt++;}}return cnt>=2;}
};
二、找出出现至少三次的最长特殊子字符串I&II
由于下一题和这一题一样,只是数据范围不同,我就一起讲了
这题其实是个分类讨论的题目,不是很难,但是要把细节想清楚,具体的分析如下:
首先弄清楚定义 --- 特殊子字符串是指由单一元素构成的字符串(要求连续),这其实就启发我们要将字符串拆分成一个个由相同字母组成的子字符串来分析问题,同时我们只关心子字符串的长度问题,所以我们可以提前处理得到由单一字母组成的子字符串长度数组
(用哈希表unordered_map<char,vector<int>>存放,具体看代码)
然后我们来分析单一字母组成的字符串长度数组该如何处理
(假设数组中的最大元素为L1,第二大元素为L2,第三大元素为L3,满足L1>=L2>=L3)
1、用数组中的一个元素(即一段字符串)来得到出现3次的特殊字符串,肯定选择最大长度的字符串,答案为L1 - 2 (这个公式是观察出来的可以举几个例子看看)
2、用数组中的两个元素(即两段字符串)来得到出现3次的特殊字符串,肯定选择最大长度和次大长度的字符串
2.1、L1=L2,答案为L1 - 1
2.2、L1>L2,答案为L2
答案为min( L1 - 1,L2 )
3、用数组中的三个元素(即三段字符串)来得到出现3次的特殊字符串,肯定选择最大长度和次大长度和第三大长度的字符串,答案为L3
(如果L1=L2=L3,答案为L3,如果L1>L2=L3,答案为L3,如果L1>L2>L3,答案为L3)
综上所诉:答案为max(L1,min( L1 - 1,L2 ),L3)
【注意】上面所有的结论均是观察出来的
考虑到不是所有的数组大小都>=3,我们可以提前往数组中加入两个0,方便后面代码的书写
代码如下
class Solution {
public:int maximumLength(string s) {int n=s.size();unordered_map<char,vector<int>>mp;char x=s[0];int len=0;for(int i=0;i<n;i++){if(x==s[i]) len++;else{mp[x].push_back(len);len=1;x=s[i];}}mp[x].push_back(len);int ans=0;for(auto it=mp.begin();it!=mp.end();++it){auto& v=it->second;v.push_back(0);v.push_back(0);sort(v.begin(),v.end(),greater<int>());int L1=v[0],L2=v[1],L3=v[2];int res=max(L1-2,max(min(L1-1,L2),L3));ans=max(ans,res);}return ans?ans:-1;}
};
三、回文串重新排列查询
这题是大模拟,里面用到一些小技巧,单个来看它们不是很难,但要把它们组合在一起就不简单了
题目意思很明确,就是一个字符串,告诉你它的左右两边各有一段区间可以"修改",要你判断是否能将它变成回文串,返回所有查询的结果
技巧一:我们可以将字符串的左右两个部分拆分开来,然后将右边的字符串翻转一下,那么现在的问题就成了能否让两个字符串相等(由于题目给的字符串长度为偶数,不用担心中间元素如何处理的问题)
代码如下
class Solution {
public:vector<bool> canMakePalindromeQueries(string str, vector<vector<int>>& queries) {//技巧一int n=str.size()/2;string s=str.substr(0,n),t=str.substr(n);reverse(t.begin(),t.end());//预处理//技巧二vector<int>dif(n+1);for(int i=0;i<n;i++) dif[i+1]=dif[i]+(s[i]!=t[i]);//技巧三vector<vector<int>>pre_s(26,vector<int>(n+1));auto pre_t=pre_s;for(int i=0;i<n;i++){for(int j=0;j<26;j++){pre_s[j][i+1]=pre_s[j][i];pre_t[j][i+1]=pre_t[j][i]; }int si=s[i]-'a',ti=t[i]-'a';pre_s[si][i+1]++;pre_t[ti][i+1]++;}auto substract=[&](int l,int r)->bool{for(int i=0;i<26;i++){if(pre_s[i][r+1]-pre_s[i][l]!=pre_t[i][r+1]-pre_t[i][l])return false;}return true;};auto check=[&](int sl,int sr,int tl,int tr,vector<vector<int>>&pre_s,vector<vector<int>>&pre_t)->bool{if(dif[sl]||dif[n]-dif[max(tr,sr)+1])//[0,sl) [tr+1,n)区间内的字符严格相等---是三种情况的共同条件,这里把它放到最前面判断return false;if(sr<tl){return dif[tl]-dif[sr+1]==0&&substract(sl,sr)&&substract(tl,tr);}else{if(tr<=sr){return substract(sl,sr);}else{vector<int>tmp1(26),tmp2(26);for(int i=0;i<26;i++){tmp1[i]=pre_s[i][sr+1]-pre_s[i][sl];tmp2[i]=pre_t[i][tr+1]-pre_t[i][tl];tmp1[i]-=pre_t[i][tl]-pre_t[i][sl];tmp2[i]-=pre_s[i][tr+1]-pre_s[i][sr+1];if(tmp1[i]<0||tmp2[i]<0)//这里一定要判断return false;}return tmp1==tmp2;}}};vector<bool>ans(queries.size());for(int i=0;i<queries.size();i++){int sl=queries[i][0],sr=queries[i][1];int tl=2*n-1-queries[i][3],tr=2*n-1-queries[i][2];//右边的区间需要修正一下ans[i] = sl<=tl?check(sl,sr,tl,tr,pre_s,pre_t):check(tl,tr,sl,sr,pre_t,pre_s);}return ans;}
};