LeetCode---394周赛

ops/2024/10/18 8:31:54/

题目列表

3120. 统计特殊字母的数量 I

3121. 统计特殊字母的数量 II

3122. 使矩阵满足条件的最少操作次数

3123. 最短路径中的边

一、统计特殊字母的数量I

分别统计小写字母和大写字母是否出现,然后求交集即可,这里我们可以用数组统计,但其实没必要,一共就26个字母,我们可以用26个bit位的0/1来表示字符是否存在,由于区分大小写,所以我们共需要两个int变量就行,代码如下

class Solution {
public:int numberOfSpecialChars(string word) {int cnt[2]={0};for(auto e:word){if((e>>5)&1) cnt[0] |= 1<<(e-'a'); // 小写字母的二进制表示的第5位为1,大写字母为0else cnt[1] |= 1<<(e-'A');}return __builtin_popcount(cnt[0]&cnt[1]);}
};

二、统计特殊字母的数量II

 

这题和第一题的区别是限定了大小写字母的位置关系,即小写字母必须在其对应的大写字母的前面,这个其实也很容易,我们只要统计不同小写字母出现的最靠后的位置和不同大写字母出现的最靠前的位置,来看对应的大小写字母是否符合条件即可,代码如下

class Solution {
public:int numberOfSpecialChars(string word) {int n = word.size();vector<int>first(26,-1);//记录大写字母出现最靠前的位置vector<int>last(26,-1);//记录小写字母出现最靠后的位置for(int i=0;i<n;i++){char c = word[i];if((c>>5)&1) last[c-'a']=i;else {if(first[c-'A']==-1) first[c-'A']=i;}}int ans = 0;for(int i=0;i<26;i++){if(first[i]<0||last[i]<0) continue;ans += first[i]>last[i];}return ans;}
};

能不能改为一次遍历呢???实际上是否满足条件符合下面这样一个状态转换关系

代码如下

class Solution {
public:int numberOfSpecialChars(string word) {int n = word.size(),cnt = 0;vector<int>status(26);for(auto e:word){ // 边进行状态转化,边统计符合条件的字母个数if((e>>5)&1){int i = e - 'a';if(status[i]==0) status[i]=1;else if(status[i]==2) status[i]=-1,cnt--;}else{int i = e - 'A';if(status[i]==0) status[i]=-1;else if(status[i]==1) status[i]=2,cnt++;}}return cnt;}
};

三、使矩阵满足条件的最少操作次数

根据题目条件, 我们知道符合条件的矩阵满足,每一列的元素都相同且相邻列的元素不同,也就是说,我们只关心每一列的元素情况,所以我们可以先统计每一列中的0-9出现的次数。题目要求最少操作次数,也就是我们最多能让多少个元素保持不变,由此我们设计出如下的状态定义:

f[i][j]表示前i列中,第i列元素为j时,最多能有多少个元素保持不变

状态转移方程:f[i+1][j] = max(f[i][k])+cnt[i][j],0<=k<=9&&k!=j

初始化:f[0][j] = 0,前0列没有元素,最多保留0个元素不变

代码如下

class Solution {
public:int minimumOperations(vector<vector<int>>& grid) {int n = grid.size(), m = grid[0].size();vector<vector<int>> cnt(m,vector<int>(10));for(int i=0;i<m;i++){for(int j=0;j<n;j++){cnt[i][grid[j][i]]++;}}vector<vector<int>> dp(m+1,vector<int>(10));// dp[i][j] 表示当前位置为j且前i列符合条件的不需要改变的最大元素个数// dp[i][j] = max(dp[i-1][k]) + cnt[i][j] k!=j 0-9for(int i=0;i<m;i++){for(int j=0;j<10;j++){int mx = INT_MIN;for(int k=0;k<10;k++){if(k==j) continue;mx = max(mx,dp[i][k]);}dp[i+1][j]=mx + cnt[i][j];}}return n*m-*max_element(dp.back().begin(),dp.back().end());}
};// 空间优化
class Solution {
public:int minimumOperations(vector<vector<int>>& grid) {int n = grid.size(), m = grid[0].size();vector<vector<int>> cnt(m,vector<int>(10));for(int i=0;i<m;i++){for(int j=0;j<n;j++){cnt[i][grid[j][i]]++;}}vector<int>f(10);for(int i=0;i<m;i++){vector<int>tmp(10);for(int j=0;j<10;j++){int mx = INT_MIN;for(int k=0;k<10;k++){if(k==j) continue;mx = max(mx,f[k]);}tmp[j]=mx + cnt[i][j];}f=tmp;}return n*m-*max_element(f.begin(),f.end());}
};

通过上述的状态定义,我们知道这题本质就是在求当前选择0-9中的几时,保留下来的数最多,由于相邻的列元素不同这一条件,其实我们只要维护前i-1列中保留下来的数的最大值和次大值即可,只有这两个值才会对第i列的状态最值产生影响【类似于我们如果只求数组中的最大值,就没必要将整个数组排序,只要遍历一边即可】,代码如下

class Solution {
public:int minimumOperations(vector<vector<int>>& grid) {int n = grid.size(), m = grid[0].size();vector<vector<int>> cnt(m,vector<int>(10));for(int i=0;i<m;i++){for(int j=0;j<n;j++){cnt[i][grid[j][i]]++;}}int f0 = 0, f1 = 0;int pre = -1;for(int i=0;i<m;i++){int mx = 0, mx2 = 0, x;for(int j=0;j<10;j++){int res = (j==pre?f1:f0) + cnt[i][j];if(res > mx) mx2 = mx, mx = res, x = j;else if(res > mx2) mx2 = res;}f0 = mx, f1 = mx2, pre = x;}return n*m-f0;}
};

四、最短路径中的边

这题就是找最短路径经过的边,本质还是求最短路径(用Dijkstra算法),只不过需要从最短路径逆推出它可能经过的边,代码如下

class Solution {
public:vector<bool> findAnswer(int n, vector<vector<int>>& edges) {vector<vector<tuple<int,int,int>>> g(n);int m = edges.size();for(int i=0;i<m;i++){auto e = edges[i];int x = e[0], y = e[1], w = e[2];g[x].emplace_back(y,w,i);g[y].emplace_back(x,w,i);}vector<long long>dist(n,LLONG_MAX);dist[0] = 0;priority_queue<pair<long long,int>> pq; // [d,i]pq.emplace(0,0);while(pq.size()){auto [d,i] = pq.top(); pq.pop();if(-d!=dist[i]) continue;for(auto [y,w,_]:g[i]){if(dist[y]>dist[i]+w){dist[y] = dist[i]+w;pq.emplace(-dist[y],y);}}}vector<bool> ans(m);if(dist[n-1]==LLONG_MAX)return ans;vector<bool> vis(n);function<void(int)>dfs=[&](int x){vis[x] = true;for(auto[y,w,i]:g[x]){if(dist[y]+w!=dist[x])continue;ans[i] = true;if(!vis[y]) dfs(y);}};dfs(n-1);return ans;}
};

http://www.ppmy.cn/ops/21134.html

相关文章

微信小程序使用echarts组件实现饼状统计图功能

微信小程序使用echarts组件实现饼状统计图功能 使用echarts实现在微信小程序中统计图的功能&#xff0c;具体的实现步骤思路可进我主页查看我的另一篇博文https://blog.csdn.net/weixin_45465881/article/details/138171153进行查看&#xff0c;本篇文章主要使用echarts组件实…

electron ipcRenderer.invoke 和 ipcMain.handle 介绍

ipcMain.handle 是 Electron 主进程中的一个方法&#xff0c;用于处理从渲染进程发送过来的 IPC 请求&#xff0c;并返回一个 Promise。渲染进程可以使用 ipcRenderer.invoke 方法发送 IPC 请求到主进程&#xff0c;并等待主进程处理完成后返回结果。 在主进程中 (main.ts)&am…

AI作画算法原理详解:从数据到艺术的自动化之旅

AI作画算法原理详解&#xff1a;从数据到艺术的自动化之旅 在数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;技术正逐步渗透到各个领域&#xff0c;其中AI作画技术更是引发了广泛关注。本文将详细解析AI作画算法的原理&#xff0c;带领读者了解从数据收集与处理到…

在RISC-V64架构的CV1811C开发板上应用perf工具进行多线程程序性能分析及火焰图调试

CV1811C环境编译 SDK目录结构 . ├── build // 编译目录,存放编译脚本以及各board差异化配置 ├── buildroot-2021.05 // buildroot开源工具 ├── freertos // freertos系统 ├── fsbl // fsbl启动固件,prebuilt形式存在…

【MySQL】——用户和权限管理(一)

&#x1f4bb;博主现有专栏&#xff1a; C51单片机&#xff08;STC89C516&#xff09;&#xff0c;c语言&#xff0c;c&#xff0c;离散数学&#xff0c;算法设计与分析&#xff0c;数据结构&#xff0c;Python&#xff0c;Java基础&#xff0c;MySQL&#xff0c;linux&#xf…

JDBC查询大数据时怎么防止内存溢出-流式查询

文章目录 1.前言2.流式查询介绍3.使用流式查询3.1不开启流式查询的内存占用情况3.2开启流式查询的内存占用情况 4.开启流式查询的注意点 1.前言 在使用 JDBC 查询大数据时&#xff0c;由于 JDBC 默认将整个结果集加载到内存中&#xff0c;当查询结果集过大时&#xff0c;很容易…

《深入浅出.NET框架设计与实现》笔记1——.NET CLI 概述

.NET CLI&#xff08;NET 命令行接口&#xff09;工具是用于开发生成运行和发布.NET应用程序的跨平台工具链。 一、CLI命令 默认安装的命令有 1、基本命令 new restore build publish run test vstest pack migrate clean sln help store 2、项目修改命令 add package add …

安装vue cli 和 安装失败的解决方式

安装vue cli 安装node.js 进入node官网下载 输入命令node -v可查看是否安装成功 使用npm安装vue cli工具 输入命令npm install -g vue/cli 如果显示当前操作系统登录的用户权限不足&#xff0c;使用以下命令 sudo npm install -g vue/cli 安装成功后 输入命令vue --versio…