T1:最长平衡子字符串:
给你一个仅由 0
和 1
组成的二进制字符串 s
。
如果子字符串中 所有的 0
都在 1
之前 且其中 0
的数量等于 1
的数量,则认为 s
的这个子字符串是平衡子字符串。请注意,空子字符串也视作平衡子字符串。
返回 s
中最长的平衡子字符串长度。
子字符串是字符串中的一个连续字符序列。
解:
1.关键:
(1)主要还是 贪心的 过程
(2)遍历一次,每次记录当前的 '0' 或者 '1'
(3)具体情况,还是要分情况讨论,具体见代码,别忘了当s[n-1] == '1' 时的边界情况即可
2.代码:
class Solution {
public:
int findTheLongestBalancedSubstring(string s) {
//所有'0' 都在 '1'之前, 而且 0的数量必须等于1的数量,return cnt*2
//直接 贪心遍历即可
int cnt_0 = 0,cnt_1 = 0 , ans = 0;
int n = s.size();
//--
for(int i=0;i0 && s[i-1] == '1' && s[i] == '0') 此时:更新ans并且刷新 cnt_0 和 cnt_1
if(i>0 && s[i-1] == '1' && s[i] == '0')
{
cnt_0--;
int num = min(cnt_0,cnt_1);
ans = max(ans,num);
cnt_0 = 1;
cnt_1 = 0; //刷新
}
}
//最后一个位置还是需要考虑一下的:
if(s[n-1] == '1')
{
int num = min(cnt_0,cnt_1);
ans = max(ans,num);
}
return ans*2;
}
};
T2:转换为二维数组
给你一个整数数组 nums
。请你创建一个满足以下条件的二维数组:
nums
中的元素。返回结果数组。如果存在多种答案,则返回其中任何一种。
请注意,二维数组的每一行上可以存在不同数量的元素。
解:
1.关键:
之前没有读懂题目,其实就是 一行中每个元素都不同
直接利用一个map容器,记录每个元素nums[i] 重复的次数
好吧,本质上就是贪心算法 中的 “木桶 短板理论”,
行数 : 由重复最多的哪个元素的 重复次数决定
(1)先预处理,将答案存到二维数组ans中,用map1记录
(2)从头 对nums数组进行遍历,并且分2中情况进行讨论
<1>这个元素nums[i]重复次数比max还要多,需要建立 新的一行
<2>否则,这个元素可以到ans[map1[nums[i]]].push_back(nums[i])中即可
2.代码:
class Solution {
public:
vector> findMatrix(vector& nums) {
//我.。。,之前没有读懂题目,其实就是 一行中每个元素都不同
//直接利用一个map容器,记录每个元素nums[i] 重复的次数
//好吧,本质上就是贪心算法 中的 “木桶 短板理论”,
//行数 : 由重复最多的哪个元素的 重复次数决定
//1.预处理:
vector> ans;
unordered_map map1;// ,打擂台,找到cnt中的那个最大值
int max = 0;
int n= nums.size();
//2.从头 对nums数组遍历一次
for(int i=0;i max)
{
max = map1[nums[i]];
vector new_col;
new_col.push_back(nums[i]); //把这个元素加入进去
//把这个行加入到ans中
ans.push_back(new_col);
}
//(2)否则,这个元素可以到ans[map1[nums[i]]].push_back(nums[i])中即可
else
{
ans[map1[nums[i]]-1].push_back(nums[i]); //(次数-1)等于对应行的下标
}
}
return ans;
}
};
T3:老鼠 和 奶酪
有两只老鼠和 n
块不同类型的奶酪,每块奶酪都只能被其中一只老鼠吃掉。
下标为 i
处的奶酪被吃掉的得分为:
reward1[i]
。reward2[i]
。给你一个正整数数组 reward1
,一个正整数数组 reward2
,和一个非负整数 k
。
请你返回第一只老鼠恰好吃掉 k
块奶酪的情况下,最大 得分为多少。
解:
1.关键:
又是一个贪心的问题: 只要计算reward1[i] 和 reward2[i] 的差值数组diff[i]即可
(1)因为需要取k个reward1,所以diff[i] = reward1[i] - reward2[i]
(2)然后,找到diff[i]中最大的那k个, 求和sum_diff
直接对diff进行排序即可,然后从后面取到k个元素的和
(3)计算 reward2中的所有和 sum2, 最终 ans = sum2 + sum_diff
总的来说,就一点,尽可能的让第一只老鼠吃到的那k个奶酪“赚得相对于让第二只老鼠来吃 更多一些”
2.代码:
class Solution {
public:
int miceAndCheese(vector& reward1, vector& reward2, int k) {
//简单,又是一个贪心的问题: 只要计算reward1[i] 和 reward2[i] 的差值数组diff[i]即可
//因为需要取k个reward1,所以diff[i] = reward1[i] - reward2[i]
//然后,找到diff[i]中最大的那k个, 求和sum_diff
//直接对diff进行排序即可,然后从后面取到k个元素的和
//计算 reward2中的所有和 sum2, 最终 ans = sum2 + sum_diff
int n = reward1.size();
int sum_diff = 0;
int sum2 = 0;
vector diff(n,0);
for(int i=0;i=(n-k);i--)
{
sum_diff += diff[i];
}
return sum2+sum_diff;
}
};
T4:最少翻转操作数
给你一个整数 n
和一个在范围 [0, n - 1]
以内的整数 p
,它们表示一个长度为 n
且下标从 0 开始的数组 arr
,数组中除了下标为 p
处是 1
以外,其他所有数都是 0
。
同时给你一个整数数组 banned
,它包含数组中的一些位置。banned
中第 i 个位置表示 arr[banned[i]] = 0
,题目保证 banned[i] != p
。
你可以对 arr
进行 若干次 操作。一次操作中,你选择大小为 k
的一个 子数组 ,并将它 翻转 。在任何一次翻转操作后,你都需要确保 arr
中唯一的 1
不会到达任何 banned
中的位置。换句话说,arr[banned[i]]
始终 保持 0
。
请你返回一个数组 ans
,对于 [0, n - 1]
之间的任意下标 i
,ans[i]
是将 1
放到位置 i
处的 最少 翻转操作次数,如果无法放到位置 i
处,此数为 -1
。
i
,ans[i]
相互之间独立计算。解:
1.关键:
备注这里借鉴了 妙蛙种子 的思路,添加了一些个人的理解
(1)先考虑特殊情况,k==1时,不能跳到其它位置
(2)将banned数组转移到 一个set中,方便访问
(3)把除了p下标位置 和 ban中的下标位置,分奇偶放到2个set容器中:
之所以这么做,就是因为 每次跳跃的区间向右移动一次后,从当前的下标位置now可以达到的下一个位置now' 其实向左 移动了2个位置,所以,对于任何一个k值,先看now+k-1也就是向右最远可以跳到的下标位置时 奇数 还是 偶数,之后就只能在 奇数或者 偶数容器set中去找下一个可以到达的位置
注意:只要在 最左边可以到达的位置 和 最右边可以到达的位置之间的 所有的奇数 或者 偶数 set中的位置,从当前的now位置下一次跳跃都是 可以到达的!!!
(4)利用一个 队列容器q进行bfs 广搜:
初值:起点位置 --就是p点下标,先入队 ,-- 一下解释 bfs的while循环的具体代码的含义
<1>取出队首元素 , 并且记得出队
<2>先Left可以移动 和 向Right可以移动的 最大距离:
<3>//悟了:之所以计算x = (now+k-1)%2作为st的下标
//是因为,从now这个点可以到达的点之间 都是相隔2位的,这一点见csdn笔记
//从st[x]中找到刚好大于now+L的位置
//然后,从左 到右,然所有now可以到达的位置全部到达一次
2.具体代码如下:
class Solution {
public:
vector minReverseOperations(int n, int p, vector& banned, int k) {
//审题:
//第一:只有下标 p 位置为1
//第二:banned[i]这个下标位置 永远不能为1
//第三:每一次操作,选择一个大小为k的子数组,并反转它
//保证,每次反转后,这个唯一的“1”还是不会到达banned[i]这个下标
//也就是说arr[banned[i]] == 0 恒成立
//第四:返回一个一维数组vector ans
//ans[i]--的含义是 将唯一的1 移动到下标i位置需要的最少“操作次数”
//不能在某一次移动的结果中让banned[i]位置变成1
//如果做不到,ans[i] = -1即可
//开始动手:
//思路:算了,先借鉴 妙蛙种子的 思路好了
//(1)特殊
vector ans(n,-1);
if(k == 1)
{
ans[p] = 0;
return ans;
}
//(2)将banned数组转移到 一个set中,方便访问
unordered_set ban;
for(auto item:banned)
{
ban.insert(item);
}
//(3)把除了p下标位置 和 ban中的下标位置,分奇偶放到2个set容器中:
set st[2];
for(int i=0;i q;
ans[p] = 0; // 初值:起点位置(相当于 一个跳跃游戏)
q.push(p);
//while循环进行搜索 和 update
while(!q.empty())
{
//<1>取出队首元素 , 并且记得出队
int now = q.front(); q.pop();
//<2>先Left可以移动 和 向Right可以移动的 最大距离:
int L = max(-(k-1) , k-1 - 2*now);
int R = min(k-1 ,(n-1)-(k-1)+(n-1)-2*now);
//<3>
//悟了:之所以计算x = (now+k-1)%2作为st的下标:
//是因为,从now这个点可以到达的点之间 都是相隔2位的,这一点见csdn笔记
int x = (now+k-1)%2;
//从st[x]中找到刚好大于now+L的位置
auto it =st[x].lower_bound(now+L);
//然后,从左 到右,然所有now可以到达的位置全部到达一次
while(it!= st[x].end())
{
if(*it > now+R)
{
break; //已经超出可以到达的 最右边的位置了
}
//访问这个位置,并且更新ans,同时从st[x]中删除这个位置
ans[*it] = ans[now] + 1;
q.push(*it); //入队。作为之后的起点
it = st[x].erase(it);//删除后,返回下一个位置的 迭代器
}
}
return ans;
}
};
总结part:
这一次明显在思维上有所进步,能够独立思考出 很多东西,而且我发现,如果一边嗑巴西松子一边思考,更容易 集中注意的 思考出大体的思路!非常美妙