栈+贪心
lc2070
二分条件为 items[mid][0] ≤ t 后,l=mid+1。 l 最终指向“第一个价格大于 t 的物品下标”
有效区间是 [0, l-1] ,此时最大美丽值为 pmx[l-1]
class Solution {
/*
输入:items = [[1,2],[3,2],[2,4],[5,6],[3,5]], queries = [1,2,3,4,5,6]
输出:[2,4,5,5,6,6] p,b
二分 小于等于后 预存mx
*/
public:
vector<int> maximumBeauty(vector<vector<int>>& items, vector<int>& queries)
{
sort(items.begin(),items.end(),[&](vector<int>& a,vector<int>& b)
{
if(a[0]==b[0])
return a[1]>b[1];
return a[0]<b[0];
});
int n=items.size();
vector<int> pmx(n,0);
pmx[0]=items[0][1];
for(int i=1;i<n;i++)
{
pmx[i]=max(pmx[i-1],items[i][1]);
}
int m=queries.size();
vector<int> ret(m,0);
for(int k=0;k<m;k++)
{
int t=queries[k];
int l=0,r=n-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(items[mid][0]>t)
r=mid-1;
else
l=mid+1;
}
ret[k] = (l == 0) ? 0 : pmx[l-1];
}
return ret;
}
};
lc2554
class Solution {
/*
输入:banned = [1,6,5], n = 5, maxSum = 6
输出:2
解释:你可以选择整数 2 和 4 。
2 和 4 在范围 [1, 5] 内,且它们都不在 banned 中,它们的和是 6 ,没有超过 maxSum 。
*/
public:
int maxCount(vector<int>& banned, int n, int maxSum)
{
int sum=0,cnt=0;
set<int> set;
for(auto& b:banned)
set.insert(b);
for(int i=1;i<=n;i++)
{
if(set.count(i))
continue;
sum+=i;
if(sum<=maxSum)
cnt++;
else break;
}
return cnt;
}
};
lc1616
双指针分别检查“a前缀+b后缀”和“b前缀+a后缀”的外层字符匹配性,再验证中间剩余段是否为回文,以此判断能否构成回文串
return isPalindrome(s1, l, r) || isPalindrome(s2, l, r);
(当外层字符都匹配后,中间剩下的区间要么在 s1 里、要么在 s2 里,只要其中一段自己是回文,整个拼接串就满足回文条件,所以只需检查这两种情况其一即可
class Solution {
public:
bool checkPalindromeFormation(string a, string b) {
int n = a.size();
// 检查 a前缀 + b后缀 是否为回文
if (check(a, b)) return true;
// 检查 b前缀 + a后缀 是否为回文
if (check(b, a)) return true;
return false;
}
bool check(string &s1, string &s2) {
int l = 0, r = s1.size() - 1;
while (l < r) {
// 先匹配s1的前缀和s2的后缀
if (s1[l] != s2[r]) break;
l++;
r--;
}
// 此时中间剩余的部分可以是s1的中间或s2的中间,只需其中一段是回文即可
return isPalindrome(s1, l, r) || isPalindrome(s2, l, r);
}
bool isPalindrome(string &s, int l, int r) {
while (l < r) {
if (s[l] != s[r]) return false;
l++;
r--;
}
return true;
}
};
lc962 栈+贪心
attention:为了找到最大宽度,需要让坡底尽可能靠左,坡顶尽可能靠右
单减栈存储潜在“坡底”索引(为什么是单减栈,可以这么理解:既然idx变大了,那我为什么要选这个元素,当然是因为它更小了👆🏻),再反向遍历数组找能形成“坡”的最右“坡顶”
(可以同理的理解为什么要反向遍历,因为我们想要去取到更右边的元素为坡顶)
while (!st.empty() && nums[j] >= nums[st.top()]) { //beautiful
ret = max(ret, j - st.top());
反向遍历坡底也可以这么理解
更右边的元素都可以够到这个左边的元素了,要是后面再--的话,还够不到这个元素,那就说明它不是最优的,无需在意🖐🏻
class Solution {
public:
int maxWidthRamp(vector<int>& nums)
{
int n = nums.size();
stack<int> st;
for (int i = 0; i < n; i++) {
if (st.empty() || nums[i] < nums[st.top()])
st.push(i);
}
int ret = 0;
// 反向遍历找最大宽度
for (int j = n - 1; j >= 0; j--) {
while (!st.empty() && nums[j] >= nums[st.top()]) { //beautiful
ret = max(ret, j - st.top());
st.pop();
}
}
return ret;
}
};
单增栈 递增 反向构建 正向遍历找坡底 坡顶靠右,正向找最左的坡底
单调递增栈存储可能的坡顶(j) 索引,然后正向遍历找坡底(i)
class Solution {
public:
int maxWidthRamp(vector<int>& nums) {
int n = nums.size();
stack<int> st; // 存储单调递增的索引(坡顶候选)
// 构建单调递增栈(从后往前构建,保证栈顶是最靠右的大值)
for (int j = n - 1; j >= 0; j--) {
if (st.empty() || nums[j] > nums[st.top()]) {
st.push(j);
}
}
int ret = 0;
// 正向遍历找坡底
for (int i = 0; i < n; i++) {
while (!st.empty() && nums[i] <= nums[st.top()]) {
ret = max(ret, st.top() - i);
st.pop();
}
}
return ret;
}
};
