Acwing第78场周赛

T1:4719. 商品种类

 第一题,依旧水

利用了set的唯一性,由于题意说字符串不超过十个字符,就补全后加入set就好

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int n;
    cin >> n;
    unordered_set<string> st;
    while(n -- )
    {
        string a, b;
        cin >> a >> b;
        while(a.size() < 10) a = a + ' ';
        while(b.size() < 10) b = b + ' ';
        st.insert(a + b);
    }
    cout << st.size() << endl;
    
    return 0;
}

再来一个,STL真好用

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int n;
    cin >> n;
    set<pair<string, string>> s;
    while(n -- )
    {
        string a, b;
        cin >> a >> b;
        s.insert({a, b});
    }
    
    cout << s.size() << endl;
    return 0;
}

set判重yyds

T2:4720. 字符串

 总感觉在哪见过,题目思路也比较简单

在这里用栈来写好一点,从左往右数,找到一个删一个

想起来了,有点类似于祖玛的游戏哈哈哈哈哈

在这里我其实想证明一下为什么题目中说的

不论按何种顺序删除相同连续字母对,最终得到的字符串都是一样的

我们用数学归纳法

n = 0 || n = 1时,此时字符串都不能删,那么结果唯一确定,成立

n<k时成立,证明n = k时也成立

我们证明用任意一种解决方式都和用栈的结构相同,

用栈的话第一个删除的就是最左边的一对

用任意方式的话就有就有几种情况:

1,和栈完全一样,这样栈得到的字符串和用任意方式的相同

2,和栈不一样,但有公用部分,删完还是一样

3,和栈不一样,无公用部分,删除后就变成k-2个字符串,又n<k时成立,结果还是一样

这证明还是比较难的。。。。。。。,看不懂的话记结论就行。

下面用看看栈的代码:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int main()
{
    string str;
    cin >> str;
    
    string res;
    for(auto c: str)
        if(res.size() && res.back() == c) res.pop_back();
        else res += c;

    cout << res << endl;
    return 0;
}

 T3:4721. 排队

这题其实和leetcode第90场双周赛T4做法类似,

我最后也写个解法

说回本题,题目本身意思很明确,关键在于解法:单调队列+二分

对于本题,我们要考虑的是对于数组每个元素,找到它右边最小的元素

这样我们就模拟一个情况:

 假如我们的目标元素是a_{k},找到a_{k}右边最小的元素

这时我们有 a_{i}  和 a_{j} ,此时假如 a_{i}\geq a_{j} 那么 a_{i} 就没有存在的必要了,因为我们的目标是 找到a_{k}右边最小的元素,所以在这里 a_{i} 就可以删除

删掉之后我们就可以发现,后面的数就具有单调递增的性质,即前面一个数大于后面一个数那么前面一个数就没有了。

之后在单调的数组中找到它右边最小的元素,二分就好。

最后就变成这样一个栈,栈顶元素最小,最后单调递增。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 100010;

int n;
int w[N], stk[N];
int ans[N];

int main()
{
    scanf("%d", &n);
    for(int i = 0; i < n; i ++ ) scanf("%d", &w[i]);
    
    int top = 0;//定义栈顶
    for(int i = n - 1; i >= 0; i -- )//从后往前枚举
    {
        if(!top || w[i] <= w[stk[top]]) ans[i] = -1;
        //如果后面没有最小值比栈顶元素小,就输出-1
        //此时最小值是栈顶
        else
        {
            int l = 1, r = top;
            while(l < r)
            {
                int mid = l + r >> 1;
                if(w[stk[mid]] < w[i]) r = mid;
                //如果发现当前元素是大于中间元素的话,改变边界R
                else l = mid + 1;
            }
            ans[i] = stk[r] - i - 1;
        }
        
        if(!top || w[i] < w[stk[top]]) stk[ ++ top] = i;
        //如果发现栈顶元素是空的话,或者栈顶元素比自己大的时候,就更新一下栈
    }
    
    for(int i = 0; i < n; i ++ )    printf("%d ", ans[i]);
    
    return 0;
}

 贴一个自己理解

现在看看2454. 下一个更大元素 IV

对于这题,我们要找的是后面第二大的数。

求后面第一大的数,可以想到acwing中算基的单调栈的做法。

在这里我们换个思路来看看

咱们从大到小枚举,在遍历到每个数的时候,比它大的数字都被标记出来

我们利用一个set来找到第二大的下标,upper_bound一次,再++

class Solution {
public:
    vector<int> secondGreaterElement(vector<int>& nums) {
        int n = nums.size();

        vector<int> res(n, -1);
        //答案先都赋为-1
        vector<int> p;

        for(int i = 0; i < n; i ++ ) p.push_back(i);
        sort(p.begin(), p.end(), [&](int x, int y){return nums[x] > nums[y];});
        //插入下标并按大小排序
        set<int> S;
        S.insert(n + 1), S.insert(n + 2);
        //处理边界条件,在最右端插入两个位置

        for(int i = 0; i < n; i ++ ){
            int j = i + 1;
            while(j < n && nums[p[j]] == nums[p[i]]) j ++ ;
            //出现数值相同的情况需要统一处理,就会出现相同的数字,这时候就要跳过

            for(int k = i; k < j; k ++ )
            {
                auto it = S.upper_bound(p[k]);//比当前下标大的第一个下标
                ++ it;
                if(*it < n) res[p[k]] = nums[*it];
            }

            for(int k = i; k < j; k ++ )
                S.insert(p[k]);
            
            i = j - 1;
            //这里下标更新要注意
        }
        return res;
    }
};

单调栈其实也可以做

1,先使用单调栈s1找到每个元素后一个更大的数字,在此基础上,每当单调栈s1出栈时,将其放入另一个单调栈s2中,接着等待元素后第二大的数字。

2,用s2存弹出等待的数字,其出栈时第二大的元素就是 nums(i) ,然后更新s1,弹栈并逆序压入s2

3,最后将nums(i) 压入s1。

class Solution {
public:
    vector<int> secondGreaterElement(vector<int>& nums) {
        const int n = nums.size();
        vector<int> ans(n, -1);

        stack<int> s1, s2;
        for (int i = 0; i < n; i++) {
            while (!s2.empty() && nums[i] > nums[s2.top()]) {
                ans[s2.top()] = nums[i];
                s2.pop();
            }

            vector<int> t;
            while (!s1.empty() && nums[i] > nums[s1.top()]) {
                t.push_back(s1.top());
                s1.pop();
            }

            s1.push(i);

            for (int j = t.size() - 1; j >= 0; j--)
                s2.push(t[j]);
        }

        return ans;
    }
};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值