刷题周总结2023.11.2 - 2023.11.8

2023.11.02

LC2103 环和杆

  • 题目链接

  • 解题思路:

    • 遍历字符串,用一个cnt数组记录每个位置颜色出现的情况
    • RGB分别对应出现情况的后三位
    • 统计出现情况为7的位置个数
  • 解题代码:

class Solution {
public:
    int countPoints(string rings) {
        vector<int> cnt(10, 0);
        int n = rings.size();
        for(int i = 0; i < n; i += 2)
        {
            int idx = rings[i+1] - '0';
            if(rings[i] == 'R')
                cnt[idx] |= 1 << 0;
            else if(rings[i] == 'G')
                cnt[idx] |= 1 << 1;
            else
                cnt[idx] |= 1 << 2;
        }
        return count(cnt.begin(), cnt.end(), 7);
    }
};

CF1679D Toss a Coin to Your Graph…

  • 题目链接

  • 解题思路:

    • 最小化最大值 => 二分答案

    • 二分枚举当前可以使用的最大值,将符合条件的顶点构成一张新的有向图,判断是否满足条件

      • 如果新图中有环,可以组合成任意长度的路径,因此直接返回true
      • 否则,计算新图中的最长路径长度,如果其大于等于k,返回true,否则返回false
    • 如果枚举了所有情况都不存在满足条件的图,返回-1

  • 解题代码:


#include<bits/stdc++.h>
using namespace std;
int main()
{
    long long n, m, k;
    cin >> n >> m >> k;
    vector<int> a(n);
    for(int i = 0; i < n; i++)
        cin >> a[i];
   
    vector<vector<int>> g(n);
    for(int i = 0; i < m; i++)
    {
        int u, v;
        cin >> u >> v;
        u -= 1;
        v -= 1;
        g[u].push_back(v);
    }
    auto check = [&](int m) -> bool
    {
        vector<int> deg(n, 0);
        vector<int> longestPath(n, 1);
       
        for(int u = 0; u < n; u++)
        {
            for(auto &v : g[u])
            {
                if(a[u] <= m && a[v] <= m)//有效边
                    deg[v] += 1;
            }
        }
        queue<int> q;
        for(int i = 0; i < n; i++)
        {
            if(a[i] <= m && deg[i] == 0)
                q.push(i);
        }
        while(!q.empty())
        {
            auto u = q.front();
            q.pop();
            for(auto &v : g[u])
            {
                if(a[v] > m)
                    continue;
                longestPath[v] = max(longestPath[v], longestPath[u] + 1);
                deg[v] -= 1;
                if(deg[v] == 0)
                    q.push(v);
            }
        }
        //判断是否有环,如果有环 返回true
        for(int i = 0; i < n; i++)
        {
            if(deg[i] != 0)
                return true;
        }
        //无环且最长路径 > k 返回true
        int length = *max_element(longestPath.begin(), longestPath.end());
        return length >= k;
    };
    int l = *min_element(a.begin(), a.end());
    int r = *max_element(a.begin(), a.end());
    while(l <= r)
    {
        int m = l + (r - l) / 2;
        if(check(m))
            r = m - 1;
        else
            l = m + 1;
    }
    int ans = l > *max_element(a.begin(), a.end()) ? -1 : l;
    cout << ans << endl;
    //system("pause");
    return 0;
}

2023.11.03

LC117 填充每个节点的下一个右侧节点指针 II

  • 题目链接
  • 解题思路1:
    • 传统的用队列实现的BFS
    • 层序遍历,对每层节点从左到右连起来
    • 时间复杂度On,空间复杂度On
  • 解题代码:
class Solution {
public:
    Node* connect(Node* root) {
        if(!root)
            return root;
        vector<Node*> q;
        q.push_back(root);
        while(!q.empty())
        {
            vector<Node*> cur;
            int len = q.size();
            for(int i = 0; i < len; i++)
            {
                if(i != len-1)
                    q[i]->next = q[i+1];
                if(q[i]->left)
                    cur.push_back(q[i]->left);
                if(q[i]->right)
                    cur.push_back(q[i]->right);
            }
            q = move(cur);
        }
        return root;
    }
};

  • 解题思路2:

    • 既然每一层都连接成一个链表了,那么知道链表头,就能访问这一层的所有节点
    • 从第一层开始(第一层只有一个 root\textit{root}root 节点),每次循环:
    • 遍历当前层的链表节点,通过节点的 left\textit{left}left 和 right\textit{right}right 得到下一层的节点。
    • 把下一层的节点从左到右连接成一个链表。
    • 拿到下一层链表的头节点,进入下一轮循环。
    • 时间复杂度On,空间复杂度O1
  • 解题代码:


class Solution {
public:
    Node* connect(Node* root) {
        Node *cur = root;
        while(cur)
        {
            Node *dummy = new Node();
            Node *p = dummy;
            while(cur)
            {
                if(cur->left)
                {
                    p->next = cur->left;
                    p = p->next;
                }
                if(cur->right)
                {
                    p->next = cur->right;
                    p = p->next;
                }
                cur = cur->next;
            }
            cur = dummy->next;
        }  
        return root;
    }
};

  • 解题思路3:

    • 可以用dfs
    • 用一个长度为树高的数组,记录每一层当前尾部节点
    • 再遍历到该层节点时,连在当前层尾部节点后面,并更新尾部节点
    • 时间复杂度On,空间复杂度Oh,h是树的高度
  • 解题代码:


lass Solution {
public:
    Node* connect(Node* root) {
        if(!root)
            return root;
        vector<Node*> pre;
        function<void(Node*, int)> dfs = [&](Node* node, int depth)
        {
            if(depth == pre.size())
                pre.push_back(node);
            else
            {
                pre[depth]->next = node;
                pre[depth] = node;
            }
            if(node->left)
                dfs(node->left, depth+1);
            if(node->right)
                dfs(node->right, depth+1);
        };
        dfs(root, 0);
        return root;
    }
};

CF13E Holes

  • 题目链接

  • 解题思路:

    • 我的一些碎碎念:
      • 当修改一个结点的值时,只会影响后面怎么走,不会影响其他的事情
      • 怎么感觉可以抽象成一个并查集的问题呢?power[i] 相当于有一条 (a[i] + power[i]) 指向 (a[i])的边
      • 维护成图的样子,每次操作0相当于删一条边,再加一条边
      • 操作1就相当于求该节点到根节点的距离
  • 解题代码: TLE13


#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n, m;
    cin >> n >> m;
    vector<int> a(n);
    for(int i = 0; i < n; i++)
        cin >> a[i];
    //建图
    vector<int> g(n, -1);
    for(int i = 0; i < n; i++)
    {
        int target = i + a[i];
        if(target < n)
            g[i] = target;
    }
    function<pair<int, int>(int i)> dfs = [&](int i) -> pair<int, int>
    {
        if(g[i] == -1)
            return {i, 1};
        auto [pos, cnt] = dfs(g[i]);
        return {pos, cnt+1};
    };
    for(int i = 0; i < m; i++)
    {
        int op;
        cin >> op;
        if(op == 0)
        {
            int idx, val;
            cin >> idx >> val;
            idx -= 1;
            int target = idx + val;
            if(target < n)
                g[idx] = target;
            else
                g[idx] = -1;
        }
        else
        {
            int idx;
            cin >> idx;
            idx -= 1;
            auto [pos, cnt] = dfs(idx);
            cout << pos+1 << " " << cnt << endl;
        }
    }
    //system("pause");
    return 0;
}
  • 灵神题解:
    • 分块,每块大小 sqrt(n)=316。(注:改成 500 可能更快一些)
    • 把每块看成一个单独的问题,算出这一块中的每个下标 i,在跳出这个块之前,最后一次的位置 last 和跳跃次数 jump。
    • 一开始可以倒着 O(n) 递推算出所有的 last 和 jump。
    • 对于操作 0,用 O(sqrt(n)) 的时间更新 p 所在块的 last 和 jump
    • 对于操作 1,用每一块的 last 和 jump 来快速跳跃,这样只需要 O(sqrt(n)) 就可以跳出数组。
    • 需要用scanf、printf 不然会超时
#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    vector<int> a(n);
    for(int i = 0; i < n; i++)
        scanf("%d", &a[i]);
   
    const int block = 500;
    //每个节点都保存在当前块中跳多少次,最后跳到哪里
    vector<pair<int, int>> data(n);
    auto f = [&](int i)
    {
        if(i + a[i] >= n || i / block != (i + a[i]) / block)
            data[i] = {i, 0};
        else
        {
            data[i] = data[i+a[i]];
            data[i].second += 1;
        }
    };
    for(int i = n-1; i >= 0; i--)
        f(i);
    for(int i = 0; i < m; i++)
    {
        int op, p;
        scanf("%d%d", &op, &p);
        p -= 1;
        if(op == 0)
        {
            int v;
            scanf("%d", &v);
            a[p] = v;
            for(int j = p; j >= p - p % block; j--)
                f(j);
        }
        else
        {
            int jumpCnt = 0;
            int lastPos = p;
            for(; p < n; p = lastPos + a[lastPos])
            {
                jumpCnt += data[p].second + 1;
                lastPos = data[p].first;
            }
            printf("%d %d\n", lastPos + 1, jumpCnt);
        }
    }
    //system("pause");
    return 0;
}

2023.11.04

LC421 数组中两个数的最大异或值

  • 题目链接

  • 解题思路:

    • 使用前缀树(Trie)
    • 对每个数分解二进制
    • 查询时,从高位到低位在树中查找,如果该数对应位为0,则应该尽量向当前节点为1的儿子方向查找;如果当前节点没有1儿子,则只能向0儿子方向查找
    • 插入时,从高位到低位将各数位更新到树中
  • 解题代码:


class Trie {
    public:
        Trie():children(2){}
        void insert(int num);
        int query(int num);
    private:
        vector<Trie*> children;
};
void Trie::insert(int num)
{
    Trie *cur = this;
    for(int i = 31; i >= 0; i--)
    {
        int bit = num >> i & 1;
        if(!cur->children[bit])
            cur->children[bit] = new Trie();
        cur = cur->children[bit];
    }
}
int Trie::query(int num)
{
    Trie *cur = this;
    int ans = 0;
    for(int i = 31; i >= 0; i--)
    {
        int bit = num >> i & 1;
        if(cur->children[1-bit])
        {
            ans += (1 << i);
            cur = cur->children[1-bit];
        }
        else
            cur = cur->children[bit];
    }
    return ans;
}
class Solution {
public:
    int findMaximumXOR(vector<int>& nums) {
        Trie *root = new Trie();
        int n = nums.size();
        int ans = 0;
        root->insert(nums[0]);
        for(int i = 1; i < n; i++)
        {
            ans = max(ans, root->query(nums[i]));
            root->insert(nums[i]);
        }
        return ans;
    }
};


2023.11.05

LC187 重复的DNA序列

  • 题目链接

  • 解题思路:

    • 用一个map记录字符串中各长度为10的子串的出现次数
    • 将出现次数大于1次的子串加到答案中
  • 解题代码:

    
class Solution {
public:
    vector<string> findRepeatedDnaSequences(string s) {
        int n = s.size();
        unordered_map<string, int> um;
        for(int i = 0; i <= n-10; i++)
        {
            string temp = s.substr(i, 10);
            um[temp] += 1;
        }
        vector<string> ans;
        for(auto item : um)
        {
            if(item.second > 1)
                ans.push_back(item.first);
        }
        return ans;
    }
};


2023.11.06

LC318 最大单词长度乘积

  • 题目链接

  • 解题思路:

    • 首先预处理,用一个整型数字记录各单词中出现的字母情况
    • 二重循环,枚举可能出现的单词情况,如果两个单词出现情况置相与等于0,说明两个单词不含有公共字母,使用其长度乘积更新答案
  • 解题代码:


class Solution {
public:
    int maxProduct(vector<string>& words) {
        vector<int> occurs;
        for(auto word : words)
        {
            int mask = 0;
            for(auto ch : word)
                mask |= 1 << (ch - 'a');
            occurs.push_back(mask);
        }
        int ans = 0;
        int n = words.size();
        for(int i = 0; i < n; i++)
        {
            for(int j = i + 1; j < n; j++)
            {
                if((occurs[i] & occurs[j]) == 0)
                    ans = max(ans, (int)words[i].size() * (int)words[j].size());
            }
        }
        return ans;
    }
};

CF816B Karen and Coffee

  • 题目链接

  • 解题思路:

    • 用差分数组维护各个推荐区间
    • 对差分数组求前缀和,得到各个温度的推荐次数
    • 再对推荐次数数组求前缀和,只更新推荐次数 >= k的那些温度
    • 对于每一个查询,可用上一步中计算出来的前缀和计算
  • 解题代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n, k, q;
    cin >> n >> k >> q;
    const int mx = 200001;
    vector<int> num(mx, 0);
    for(int i = 0; i < n; i++)
    {
        int l, r;
        cin >> l >> r;
        num[l] += 1;
        num[r+1] -= 1;
    }

    vector<int> origin(mx, 0);
    for(int i = 0; i < mx; i++)
        origin[i] = i == 0 ? num[i] : origin[i-1] + num[i];
    
    vector<int> s(mx+1, 0);
    for(int i = 1; i < mx+1; i++)
        s[i] = s[i-1] + (origin[i-1] >= k);
    
    for(int i = 0; i < q; i++)
    {
        int l, r;
        cin >> l >> r;
        cout << s[r+1] - s[l] << endl;
    }
    //system("pause");
    return 0;
}

2023.11.07

LC2586 统计范围内的元音字符串数

  • 题目链接

  • 解题思路:

    • 对于范围[left, right]内的每个字符串,判断其是否首字母和尾字母均为元音字母,如果是则答案+1
  • 解题代码:

class Solution {
public:
    int vowelStrings(vector<string>& words, int left, int right) {
        unordered_set<char> us = {'a', 'e', 'i', 'o', 'u'};
        int ans = 0;
        for(int i = left; i <= right; i++)
        {
            if(us.count(words[i][0]) && us.count(words[i].back()))
                ans += 1;
        }
        return ans;
    }
};

CF1106D Lunar New Year and a Wander

  • 题目链接

  • 解题思路:

    • 类似于广度优先搜索的思路
    • 用一个小根堆维护当前可以访问的结点,用vis数组记录各结点是否被访问过
    • 每次都选择访问小根堆堆顶结点,并将其相连且未被访问过的结点加到堆中
  • 解题代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n, m;
    cin >> n >> m;
    vector<vector<int>> g(n);
    for(int i = 0; i < m; i++)
    {
        int x, y;
        cin >> x >> y;
        x -= 1;
        y -= 1;
        g[x].push_back(y);
        g[y].push_back(x);
    }

    priority_queue<int, vector<int>, greater<>> pq;
    vector<bool> vis(n, false);
    pq.push(0);
    vis[0] = true;
    while(!pq.empty())
    {
        auto x = pq.top();
        pq.pop();
        cout << x+1 << " ";
        for(auto y : g[x])
        {
            if(!vis[y])
            {    
                pq.push(y);
                vis[y] = true;
            }
        }
    }
    cout << endl;
    //system("pause");
    return 0;
}

LC2262. 字符串的总引力

  • 题目链接

  • 解题思路:

    • 从左向右遍历整个字符串
    • f[i] 表示以i开头 到 当前遍历位置这个字符串有多少不同字符
    • 假设当前遍历位置为cur,其字符是ch,ch上一次出现位置为pre,那么[pre+1, cur]范围内的f[i]均需要+1
  • 解题代码:


class Solution {
public:
    long long appealSum(string s) {
        long long ans = 0;
        unordered_map<char, int> um;
        int n = s.size();
        long long temp = 0;
        for(int i = 0; i < n; i++)
        {
            if(um.count(s[i]))
                temp += i - um[s[i]];
            else
                temp += i+1;
            ans += temp;
            um[s[i]] = i;
        }
        return ans;
    }
};

2023.11.08

LC2609 最长平衡子字符串

  • 题目链接

  • 解题思路:

    • 子字符串长度一定是偶数
    • 从大到小倒序枚举子字符串偶数长度,检查是否存在满足条件的子字符串。如果存在直接返回答案
    • 如果所有长度都不存在满足条件的子字符串,返回0
  • 解题代码:


class Solution {
public:
    int findTheLongestBalancedSubstring(string s) {
        //长度是偶数的子串,且前一半全是0,后一半全是1
        int n = s.size();
        //倒序枚举长度
        int len = n & 1 ? n-1 : n;
        for(len; len > 0; len -= 2)
        {
            for(int i = 0; i < n; i++)
            {
                if(i + len > n)
                    break;
                int l = i, r = i+len-1;
                while(l <= r)
                {
                    if(s[l] != '0' || s[r] != '1')
                        break;
                    l += 1;
                    r -= 1;
                }
                if(l > r)
                    return len;
            }
        }
        return 0;
    }
};

  • 解题思路2:

    • 记录上一段连续相同字符个数pre,以及当前连续相同字符个数cur
    • 如果当前字符是1,那么上一段的字符是0,这两段可以组成一个01串,其长度为2 * min(pre, cur)
  • 解题代码:


class Solution {
public:
    int findTheLongestBalancedSubstring(string s) {
        int pre = 0;
        int cur = 0;
        int ans = 0;
        int n = s.size();
        for(int i = 0; i < n; i++)
        {
            cur += 1;
            if(i == n-1 || s[i] != s[i+1])
            {
                if(s[i] == '1')
                    ans = max(ans, 2 * min(pre, cur));
                pre = cur;
                cur = 0;
            }
        }
        return ans;
    }
};

CF1140C Playlist

  • 题目链接

  • 解题思路:

    • 按照b从大到小排序,枚举最小的b,把当前t和其左边的t都加到一个最小堆中,从而维护堆中最大的k个数的和
  • 解题代码:


#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n, k;
    cin >> n >> k;
    vector<int> t(n), b(n);
    for(int i = 0; i < n; i++)
        cin >> t[i] >> b[i];
   
   
    vector<pair<int, int>> songs;
    for(int i = 0; i < n; i++)
        songs.push_back({t[i], b[i]});
   
    sort(songs.begin(), songs.end(), [&](const pair<int, int>& a, const pair<int, int>& b)
    {
        return a.second > b.second;
    });
    long long ans = 0;
    long long sum = 0;
    priority_queue<int, vector<int>, greater<>> pq;
    for(int i = 0; i < n; i++)
    {
        if(pq.size() < k || pq.top() < songs[i].first)
        {
            pq.push(songs[i].first);
            sum += songs[i].first;
            if(pq.size() > k)
            {    
                sum -= pq.top();
                pq.pop();
            }  
        }
        ans = max(ans, sum * songs[i].second);
    }
    cout << ans << endl;
    //system("pause");
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值