LeetCode --- 434周赛

news/2025/2/5 13:56:03 标签: leetcode, 算法, 职场和发展

目录

3432. 统计元素和差值为偶数的分区方案
3433. 统计用户被提及情况
3434. 子数组操作后的最大频率
3435. 最短公共超序列的字母出现频率

一、统计元素和差值为偶数的分区方案

统计元素和差值为偶数的分区方案
本题可以直接模拟,当然我们也可以来从数学的角度来分析一下这题的本质

S S S 为数组之和, L L L [ 0 , i ] [0,i] [0,i] 的区间和,则 S − L S-L SL [ i + 1 , n − 1 ] [i+1,n-1] [i+1,n1] 的区间和,对于任意的 i i i,左右子数组的区间和差值为 L − ( S − L ) = 2 L − S L-(S-L)=2L-S L(SL)=2LS,题目问有几个 i i i 2 L − S 2L-S 2LS 为偶数,显然 2 L − S 2L-S 2LS 是否为偶数只和 S S S 有关,当 S S S 为偶数时, 2 L − S 2L-S 2LS 总是为偶数。

故本题的答案是固定的,要么所有的 2 L − S 2L-S 2LS 都是偶数,有 n − 1 n-1 n1 个,要么都不为偶数,有 0 0 0 个,代码如下

class Solution {
public:
    int countPartitions(vector<int>& nums) {
        int n = nums.size();
        int s = accumulate(nums.begin(),nums.end(), 0);
        return s % 2 ? 0 : n - 1;
    }
};

二、统计用户被提及情况

统计用户被提及情况
本题是一个纯模拟题,关键在于如何统计每个用户被提及的次数,而这取决于该用户是否在线。这里我们可以记录每一个用户的最近一次的在线时间,在用它和消息发送的时间进行比较,从而判断出当前消息是否提及该用户,代码如下

class Solution {
public:
    vector<int> countMentions(int numberOfUsers, vector<vector<string>>& events) {
        // cnt 统计答案,online 记录每个用户最近一次上线时间
        vector<int> cnt(numberOfUsers), online(numberOfUsers);
        // 按时间顺序排序,注意:题目要求离线操作优先级更高
        ranges::sort(events, [](const auto& x, const auto & y){
            int tx = stoi(x[1]), ty = stoi(y[1]);
            return tx != ty ? tx < ty : x[0][0] == 'O';
        });
        for(auto e : events){
            int time = stoi(e[1]);
            if(e[0][0] == 'O'){
                online[stoi(e[2])] = time + 60; // 跟新最新上线时间
            }else if(e[2][0] == 'A'){
                for(auto& x : cnt) x++;
            }else if(e[2][0] == 'H'){
                for(int i = 0; i < numberOfUsers; i++){
                    if(online[i] <= time){
                        cnt[i]++;
                    }
                }
            }else{
                string t;
                for(stringstream ss(e[2]); ss >> t; cnt[stoi(t.substr(2))]++);
            }
        }
        return cnt;
    }
};

三、子数组操作后的最大频率

子数组操作后的最大频率
本题题意为我们可以将任意区间内值为 x x x 的数变为 k k k,返回进行一次操作后, k k k 出现的最大频率。显然,我们要分 3 3 3 个部分统计 k k k 的出现次数

  • 假定我们让 [ l , r ] [l,r] [l,r] 内的 x x x 全部变为 k k k
  • k k k 的出现次数 = [ 0 , l ) =[0,l) =[0,l) 内的 k k k 的次数 + [ l , r ] +[l,r] +[l,r] 内的 x x x 的出现次数 + ( r , n ) +(r,n) +(r,n) 内的 k k k 的出现次数

思路:

  1. 由于 n u m s nums nums 中数字范围为 1 1 1~ 50 50 50,所以我们可以暴力枚举需要改变的数字 x x x
  2. 子数组相关的问题,可以考虑用动态规划来思考
    • f [ i ] [ 0 ] f[i][0] f[i][0] 表示当 i i i 处于 [ 0 , l ) [0,l) [0,l) 中时, [ 0 , i ] [0,i] [0,i] k k k 的最大出现次数
    • f [ i ] [ 1 ] f[i][1] f[i][1] 表示当 i i i 处于 [ l , r ] [l,r] [l,r] 中时, [ 0 , i ] [0,i] [0,i] k k k 的最大出现次数
    • f [ i ] [ 2 ] f[i][2] f[i][2] 表示当 i i i 处于 ( r , n ) (r,n) (r,n) 中时, [ 0 , i ] [0,i] [0,i] k k k 的最大出现次数
  • f [ i ] [ 0 ] = f [ i − 1 ] [ 0 ] + ( n u m s [ i ] = = k ) f[i][0] = f[i-1][0]+(nums[i]==k) f[i][0]=f[i1][0]+(nums[i]==k),因为 i ∈ [ 0 , l ) i\in[0,l) i[0,l),则 i − 1 ∈ [ 0 , l ) i-1\in[0,l) i1[0,l),所以它只能由自己转移来
  • f [ i ] [ 1 ] = m a x ( f [ i − 1 ] [ 0 ] , f [ i − 1 ] [ 1 ] ) + ( n u m s [ i ] = = x ) f[i][1] = max(f[i-1][0],f[i-1][1])+(nums[i]==x) f[i][1]=max(f[i1][0],f[i1][1])+(nums[i]==x),因为 i ∈ [ l , r ] i\in[l,r] i[l,r],则 i − 1 ∈ [ l , r ] i-1\in[l,r] i1[l,r] 或者 i − 1 = l − 1 i-1=l-1 i1=l1,所以它可以由 f [ i − 1 ] [ 0 ] f[i-1][0] f[i1][0] f [ i − 1 ] [ 1 ] f[i-1][1] f[i1][1] 转移来
  • f [ i ] [ 2 ] = m a x ( f [ i − 1 ] [ 1 ] , f [ i − 1 ] [ 2 ] ) + ( n u m s [ i ] = = k ) f[i][2] = max(f[i-1][1],f[i-1][2])+(nums[i]==k) f[i][2]=max(f[i1][1],f[i1][2])+(nums[i]==k),因为 i ∈ ( r , n ) i\in(r,n) i(r,n),则 i − 1 ∈ ( r , n ) i-1\in(r,n) i1(r,n) 或者 i − 1 = r i-1=r i1=r,所以它可以由 f [ i − 1 ] [ 0 ] f[i-1][0] f[i1][0] f [ i − 1 ] [ 1 ] f[i-1][1] f[i1][1] 转移来

代码如下

class Solution {
    // 0 1 2
    // f[i][j] 表示 i 为 j 状态时,k 出现的最大频率
    // j = 0, f[i][0] = f[i-1][0] + nums[i] == k
    // j = 1, f[i][1] = max(f[i-1][1], f[i-1][0]) + nums[i] == x
    // j = 2, f[i][2] = max(f[i-1][2], f[i-1][1]) + nums[i] == k
public:
    int maxFrequency(vector<int>& nums, int k) {
        int n = nums.size();
        set<int> st(nums.begin(), nums.end());
        int ans = 0;
        for(auto x : st){
        	vector<array<int, 3>> f(n + 1);
            for(int i = 0; i < n; i++){
                f[i+1][0] = f[i][0] + (nums[i] == k);
                f[i+1][1] = max(f[i][1], f[i][0]) + (nums[i] == x);
                f[i+1][2] = max(f[i][2], f[i][1]) + (nums[i] == k);
            }
            // 注意:答案只可能在 f[n][1] 或者 f[n][2] 中,因为修改数字总比不修改数字更优
            ans = max({ans, f.back()[1], f.back()[2]});
        }
        return ans;
    }
};

// 空间优化
class Solution {
public:
    int maxFrequency(vector<int>& nums, int k) {
        int n = nums.size();
        set<int> st(nums.begin(), nums.end());
        int ans = 0;
        for(auto x : st){
            int f[3]{};
            for(int i = 0; i < n; i++){
                f[2] = max(f[2], f[1]) + (nums[i] == k);
                f[1] = max(f[1], f[0]) + (nums[i] == x);
                f[0] = f[0] + (nums[i] == k);
            }
            ans = max({ans, f[1], f[2]});
        }
        return ans;
    }
};

四、最短公共超序列的字母出现频率

最短公共超序列的字母出现频率
在这里插入图片描述
由于 w o r d s [ i ] . l e n g t h = = 2 words[i].length==2 words[i].length==2,所以对于任意字符来说,只要它的出现次数为 2,则可以组成任意一个包含该字符的 w o r d word word前提是我们将出现了两次的字符都对称的放在两侧,比如字符 a 、 b a、b ab,如果我们的公共超序列为 a b c d e b a abcdeba abcdeba,则 a b 、 a c 、 a d 、 a e 、 b a 、 c a 、 d a 、 e a 、 a a ab、ac、ad、ae、ba、ca、da、ea、aa abacadaebacadaeaaa 所有包含 a a a 的组合都能得到,同理 b b b 也是一样

  • 简单的证明如下
  • 对于字符串 a ∗ ∗ ∗ ∗ ∗ a a*****a aa,显然能组成任意包含字符 a a a 的长度为 2 2 2 的子序列
  • 对于字符串 a b ∗ ∗ ∗ b a ab***ba abba,去掉字符 a a a 后得字符串 b ∗ ∗ ∗ b b***b bb,显然能组成任意包含 b b b 的长度为 2 2 2 的子序列,除了 a b 、 b a ab、ba abba,但是我们在计算字符 a a a 的组合时,已经计算过 a b 、 b a ab、ba abba 了,故我们能得出包含字符 a a a b b b 的任意组合的长度为 2 2 2 的子序列
  • 当字符串为 a b c ∗ ∗ ∗ c b a 、 a b c d ∗ ∗ ∗ d c b a 、 . . . abc***cba、abcd***dcba、... abccbaabcddcba... 时,同上
  • 通过数学归纳法,得出结论:如果给定的一个或多个字符的个数为 2 2 2,则我们可以通过上述的构造形式,得到长度为 2 2 2 的包含这些字符的所有子序列组合

所以最短公共超序列中每个字符的出现次数要么为 1 1 1,要么为 2 2 2。而 w o r d s words words 中最多只有 16 16 16 种不同的字符,我们可以枚举每种字符的出现次数的所有可能情况。 ( ( (最多有 2 16 2^{16} 216种可能 ) ) )

故我们的问题变为判断每种情况是否合法,即是否满足题目要求。根据上面的结论,我们只要关心出现一次的字符即可。
比如对于 a b c abc abc 这样的最短公共超序列,如果 w o r d s = { a b 、 b c 、 c a } words =\{ab、bc、ca \} words={abbcca},则不能满足条件,因为一个 a a a 不能同时出现在 c c c 的两边,我们可以将 w o r d s words words 抽象成一个有向图,其中 a b ab ab 表示 a → b a\rightarrow b ab 这样一条边,我们只要判断对于出现次数为 1 1 1 的字符,在 w o r d s words words 抽象成的有向图中是否有环即可。如果有环则不合法,反之,则合法 。

代码如下

class Solution {
public:
    vector<vector<int>> supersequences(vector<string>& words) {
        int n = words.size();
        vector<vector<int>> g(26);
        int all = 0; // 用 bit 位是否为 1,表示字符是否出现过
        for(auto s : words){
            int x = s[0] - 'a', y = s[1] - 'a';
            all |= (1 << x) | (1 << y);
            g[x].push_back(y);
        }
        auto has_cycle = [&](int sub)->bool{
            int color[26]{};
            // 判断是否有环
            auto dfs = [&](this auto&& dfs, int x)->bool{
                color[x] = 1;
                for(int y : g[x]){
                    if(sub >> y & 1) // 只关注出现次数为 1 次的字符
                        continue;
                    if(color[y] == 1 || color[y] == 0 && dfs(y))
                        return true;
                }
                color[x] = 2;
                return false;
            };
            // 有向图不一定连通,所以要字符都要遍历一遍
            for(int i = 0; i < 26; i++){
                if(color[i] == 0 && (sub >> i & 1) == 0 && dfs(i)) // 只关注出现次数为 1 次的字符
                    return true;
            }
            return false;
        };
        unordered_set<int> st;
        int min_size = INT_MAX;
        int sub = all; // sub 中用 bit 位是否为 1,表示字符是否出现了 2 次
        // 遍历字符的出现次数的所有可能情况
        do{
            int size = __builtin_popcount((unsigned) sub);
            if(size <= min_size && !has_cycle(sub)){
                if(size < min_size){
                    min_size = size;
                    st.clear();
                }
                st.insert(sub);
            }
            sub = all & (sub - 1);
        }while(sub != all);
        // 记录答案
        vector<vector<int>> ans;
        for(int sub : st){
            vector<int> cnt(26);
            for(int i = 0; i < 26; i++){
                cnt[i] = (all >> i & 1) + (sub >> i & 1);
            }
            ans.push_back(cnt);
        }
        return ans;
    }
};

http://www.niftyadmin.cn/n/5842250.html

相关文章

站在JavaScript的视角去看,HTML的DOM和GLTF的Json数据。

很多前端小伙伴没有见过、操作过gltf文件&#xff0c;对非常懵逼&#xff0c;本文从前端小伙伴最熟悉的dom模型为切入口&#xff0c;以类别的方式来学习一下gltf文件。 一、结构与组织形式 HTML DOM&#xff08;文档对象模型&#xff09;&#xff1a; 树形结构&#xff1a;HT…

结合 vim-plug 安装并使用 Gruvbox 主题教程

前置工作&#xff1a;vim-plug的安装 vim-plug的自动安装与基本使用介绍-CSDN博客 安装 Gruvbox 在 .vimrc 的plug列表中添加以下代码 call plug#begin()Plug morhetz/gruvboxcall plug#end()在vim中使用命令 :PlugInstall 配置 Gruvbox 官方文档 Configuration morhetz/…

FreeRTOS学习 --- 消息队列

队列简介 队列是任务到任务、任务到中断、中断到任务数据交流的一种机制&#xff08;消息传递&#xff09; 全局变量的弊端&#xff1a;数据无保护&#xff0c;导致数据不安全&#xff0c;当多个任务同时对该变量操作时&#xff0c;数据易受损 使用队列的情况如下&#xff1a;…

Scala语言的安全开发

Scala语言的安全开发 引言 在现代软件开发中&#xff0c;安全性是一个不可忽视的重要因素。特别是在处理敏感数据和用户信息时&#xff0c;确保代码的安全性尤为重要。Scala语言以其强大的功能和灵活性&#xff0c;在大数据处理和并发编程中受到了广泛的关注与应用。然而&…

连续的最长序列(哈希)

给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1&#xff1a; 输入&#xff1a;nums [100,4,200,1,3,2] 输出&#xff1a;4 解…

MyBatis-Plus速成指南:简化你的数据库操作流程

简介&#xff1a; MyBatis-Plus 是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。MyBatsi-Plus 提供了通用的 Mapper 和 Service&#xff0c;可以不编写任何 SQL 语句的前提下&#xff0c;快速的实现单表的增…

ip属地是根据所在位置定位的吗

在数字化时代&#xff0c;随着网络社交和电子商务的蓬勃发展&#xff0c;IP属地这一概念逐渐走入了大众的视野。许多平台开始显示用户的IP属地&#xff0c;这一举措旨在增强网络信息的透明度和真实性。然而&#xff0c;关于IP属地是否就是根据用户所在位置进行定位的问题&#…

Android 使用ExpandableListView时,需要注意哪些细节

1. 布局属性设置 尺寸属性 宽度和高度&#xff1a;要合理设置 android:layout_width 和 android:layout_height 属性。如果设置为 match_parent&#xff0c;它会填满父容器&#xff1b;设置为 wrap_content&#xff0c;则会根据内容自动调整大小。例如&#xff0c;若想让 Exp…