leetcode 404 周赛 找出有效子序列的最大长度 II「暴力」「动态规划」

3202. 找出有效子序列的最大长度 II

题目描述

给你一个整数数组 n u m s nums nums和一个正整数 k k k

n u m s nums nums 的一个子序列 s u b sub sub的长度为 x x x ,如果其满足以下条件,则称其为 有效子序列

( s u b [ 0 ] + s u b [ 1 ] ) % k = = ( s u b [ 1 ] + s u b [ 2 ] ) % k = = . . . = = ( s u b [ x − 2 ] + s u b [ x − 1 ] ) % k (sub[0] + sub[1]) \% k == (sub[1] + sub[2]) \% k == ... == (sub[x - 2] + sub[x - 1]) \% k (sub[0]+sub[1])%k==(sub[1]+sub[2])%k==...==(sub[x2]+sub[x1])%k

返回 n u m s nums nums最长有效子序列 的长度。

数据范围:

  • 2 <= nums.length <= 1 0 3 10^3 103
  • 1 <= nums[i] <= 1 0 7 10^7 107
  • 1 <= k <= 1 0 3 10^3 103

思路1:巧妙的“暴力”

这是我赛时的思路,一种比较巧妙的暴力,就是写起来有点费力

观察数据范围,n和k都是1000,显然正确答案的复杂度应该类似于O(n * k),即1000 * 1000

由于题目的要求:相邻两个数求和对k去模后结果一样,所以我们可以考虑先枚举余数p

此时问题变成了,给定余数 p p p和数组 n u m s nums nums,求一个最长的子序列,满足相邻两项求和模 k k k等于 p p p,此时考虑枚举子序列的起点 n u m s [ i ] nums[i] nums[i],由于余数 p p p是固定的,我们可以求出子序列的下一个数为 ( p − n u m s [ i ] + k ) (p - nums[i] + k)%k (pnums[i]+k),这样我们就不难发现,只要确定了第一个数字和余数p,这条最长的子序列就能确定下来

比如,1 1 2 0 1 1 2 1 2 1 2,假设k是4,当前枚举的余数p是3,只要确定了起点1,那其对应的最长的子序列就是1 2 1 2 1 2 1 2,此时我们需要一种方法可以快速获得下标大于 i i i 的某个特定数的下标,从而实现下标的跳转

虽然 n u m s [ i ] < = 1 e 7 nums[i]<=1e7 nums[i]<=1e7,但是对k取模后,数据范围就变成0到k-1了,所以我们可以考虑开一个vector数组v,对于nums[i]我们在vector数组里存下他的下标,即 v [ n u m s [ i ] % k ] . p u s h _ b a c k ( i ) v[nums[i]\%k].push\_back(i) v[nums[i]%k].push_back(i),然后再搞一个id数组,id[x]代表vector[x]现在用到的下标,这样可以避免重复遍历

我们可以再搞一个vis数组标记一下,因为我们通过跳转得到的一个子序列,从中间的任意一个元素开始跳都不会比起点开始更长,所以标记一下以后这些点就不会再重复计算了,当然还有一种情况是,存在一些中间多余的数字,比如1 1 2 1 1 2 1 2 1 2,最长的序列是1(1) 2 1 (1) 2 1 2 1 2,中间的两个1在跳转的过程中不会被遍历到,vis也不会标记它,此时id[1]和id[2]的位置也已经到了最后面,理论上从它开始跳转根本不会按正常的跳转方式得到一个完整的子序列,这种情况下是否会影响答案呢?

答案是不会影响的,看刚刚的例子,被省略的两个1,前面已经存在过1了,以1开头最长的子序列长度已经确定了,其他的1再开始也不会比他更长,所以是不会影响答案

不难发现,第二层看上去虽然是一层for一层while,但其实每个点只访问了一次,所以是O(n)的,所以这个代码的复杂度是O(n * k)

class Solution {
public:
    int maximumLength(vector<int>& nums, int k) {
        int tr[1005];
        int n = nums.size();
        vector<int>v[1005];
        for(int i = 1; i <= n; ++i){
            tr[i] = nums[i - 1];
            tr[i] %= k;
            v[tr[i]].push_back(i);
        }
        int ans = 0;
        for(int p = 0; p < k; ++p){//枚举余数
            bool vis[1005];
            memset(vis, 0, sizeof(vis));
            int id[1005];
            memset(id, 0, sizeof(id));
            for(int i = 1; i <= n; ++i){//枚举起点
                if(vis[i])continue;
                vis[i] = 1;
                int num = 1;
                int a = tr[i];
                int pos = i;
                while(1){
                    int b = (p - a + k) % k;
                    while(id[b] < v[b].size()){
                        if(v[b][id[b]] <= pos){
                            ++id[b];
                        }
                        else break;
                    }
                    if(id[b] < v[b].size() && v[b][id[b]] > pos){
                        pos = v[b][id[b]];
                        vis[pos] = 1;
                        ++num;
                        a = b;
                    }
                    else break;
                }
                ans = max(ans, num);
            }
        }
        return ans;
    }
};

思路2:dp

确定了余数p和第一个数,这个序列就确定了,会是两个数字循环

所以我们可以考虑用 d p [ n u m [ i ] ] dp[num[i]] dp[num[i]]表示以 n u m [ i ] num[i] num[i] 结尾的子序列的最长长度

转移方程很简单, d p [ n u m s [ i ] ] = d p [ ( p − n u m s [ i ] + k ) dp[nums[i]] = dp[(p - nums[i] + k) % k] + 1 dp[nums[i]]=dp[(pnums[i]+k)

class Solution {
public:
    int maximumLength(vector<int>& nums, int k) {
        int ans = 0;
        for(int p = 0; p < k; ++p){
            vector<int>dp(k, 0);
            for(int i = 0; i < nums.size(); ++i){
                nums[i] %= k;
                dp[nums[i]] = dp[(p - nums[i] + k) % k] + 1;
                ans = max(ans, dp[nums[i]]);
            }
        }
        return ans;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/775658.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【ABB】原点设定

【ABB】原点设定 操作流程演示 操作流程 操作轴回原点编辑电机校准偏移更新转速计数器 1.首先得了解机器手的轴&#xff0c;这里以6轴作参考。 注意先回456轴&#xff0c;后回123轴。 2.然后需要了解机器人关节运动模式&#xff0c;即选择如下两个模式。 3.注意机器人各轴移动…

19C 单机文件系统安装文档

准备工作 1)查看系统版本、内核参数 more /etc/redhat-release more /etc/redflag-releaseuname -a2)查看当前系统是否配置了HugePages。在下面的查询中&#xff0c;HugePages的几个相关值都为0&#xff0c;表明当前未配值HugePages&#xff0c;其次可以看到该版本的大页大小为…

Linux服务器性能参数指标

【摘要】一个基于 Linux 操作系统的服务器运行的同时&#xff0c;会表征出各种各样参数信息&#xff0c;这些蛛丝马迹往往会帮助快速定位跟踪问题。 这里只是一些简单的工具查看系统的相关参数&#xff0c;当然很多工具也是通过分析加工 /proc、/sys 下的数据来工作的&#xff…

课设:选课管理系统(Java+MySQL)

在本博客中&#xff0c;我将介绍用Java、MySQL、JDBC和Swing GUI开发一个简单的选课管理系统。 技术栈 Java&#xff1a;用于编写应用程序逻辑MySQL&#xff1a;用于存储和管理数据JDBC&#xff1a;用于连接Java应用程序和MySQL数据库Swing GUI&#xff1a;用于构建桌面应用程…

RH850系列芯片深度剖析 1.8-内存管理之MPU

RH850系列芯片深度剖析 1.8-内存管理之MPU 文章目录 RH850系列芯片深度剖析 1.8-内存管理之MPU一、MPU简介1.1 功能特性1.2 系统保护标识符(SPID)二、保护区域设置2.1 保护区域属性设置2.2 保护区域设置注意事项2.2.1 跨越保护区域边界2.2.2 无效的保护区域设置2.2.3 保护违规…

【anaconda】—“conda info“命令后conda配置和环境信息的理解

文章目录 conda配置和环境信息的理解 conda配置和环境信息的理解 安装anaconda成功后&#xff0c;打开cmd&#xff0c;输入"conda info"命令&#xff0c;结果显示如下&#xff1a; conda的配置和环境信息的输出。以下是对每个字段的解释&#xff1a; active environm…

记录一下被一行代码耽误的一下午

记录一下被一行代码耽误的一下午 代码如下&#xff1a; defineOptions({name: OrderRewards})起因使用了yudao的项目框架&#xff0c;前端页面切换之后莫名其妙重新刷新页面&#xff0c;而另外的页面则会保存检索条件 页面配置页面 设定路由的名字&#xff0c;一定要填写不然…

【HarmonyOS4学习笔记】《HarmonyOS4+NEXT星河版入门到企业级实战教程》课程学习笔记(二十二)

课程地址&#xff1a; 黑马程序员HarmonyOS4NEXT星河版入门到企业级实战教程&#xff0c;一套精通鸿蒙应用开发 &#xff08;本篇笔记对应课程第 32 节&#xff09; P32《31.通知-基础通知》 基础文本类型通知&#xff1a;briefText 没有用&#xff0c;写了也白写。 长文本类型…

[SAP ABAP] 版本管理

版本管理是指软件开发过程中各种程序代码、配置文件以及说明文档等文件变更的管理 生成版本 版本管理 对比版本 点击上述版本管理即可进行版本对比操作 补充扩展 我们可以使用事务码SE10对传输请求进行创建、修改、删除、合并以及更改所有者等操作 使用事务码SCC1进行不同cl…

CV01_相机成像原理与坐标系之间的转换

目录 0.引言&#xff1a;小孔成像->映射表达式 1. 相机自身的运动如何表征&#xff1f;->外参矩阵E 1.1 旋转 1.2 平移 2. 如何投影到“像平面”&#xff1f;->内参矩阵K 2.1 图像平面坐标转换为像素坐标系 3. 三维到二维的维度是如何丢失的&#xff1f;…

【CentOS7.6】docker部署EMQX教程,本地镜像直接导入(附下载链接),没法在云服务器上魔法拉取镜像的快来

总览 先把下载链接放在这里吧&#xff0c;这是 EMQX 的 tar 包&#xff0c;能够直接导入 CentOS 的 docker&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rSGSLoVvj83ai6d5oolg8Q?pwd0108 提取码&#xff1a;0108 一、安装配置教程 1.将 EMQX-latest.tar 包导入…

记录第一次写脚本

使用csh语言&#xff0c;Linux系统操作的 写和执行csh&#xff08;C Shell&#xff09;脚本不需要额外的软件&#xff0c;只需要一个支持csh的终端环境。 1.检查是否安装了C Shell 在终端terminal运行以下命令 which csh 如果返回路径&#xff0c;比如/bin/csh&#xff0c…

【mybatis】mybatisX插件概述

一、主要功能 智能补全与提示 MyBatisX 可以智能地提示和补全 SQL 语句中的关键字、表名、列名等信息&#xff0c;从而显著提高开发效率。代码生成器 虽然 MyBatisX 本身可能不直接提供一个完整的、独立的代码生成器&#xff0c;但它可能集成了或支持与其他代码生成工具&#…

【Linux进阶】磁盘分区2——MBR和GPT

1.磁盘的分区 因为如果你的磁盘被划分成两个分区&#xff0c;那么每个分区的设备文件名是什么&#xff1f; 在了解这个问题之前&#xff0c;我们先来复习一下磁盘的组成&#xff0c;因为现今磁盘的划分与它物理的组成很有关系。 我们谈过磁盘主要由碟片、机械手臂、磁头与主轴马…

Tomcat(+Servlet)笔记+代码

Tomcat安装和配置 安装在不含中文的路径&#xff0c;路径不能太长 Apache 官网&#x1f447; Apache Tomcat - Welcome! 配置部分 点击下图红框处&#xff0c;找到Tomcat安装位置 添加项目的文件 配好的话&#xff0c;红框这里有个猫 代码部分 新建jsp文件&#xff0c;里…

小程序渗透测试的两种方法——burpsuite、yakit

首先呢主要是配置proxifier&#xff0c;找到小程序的流量&#xff0c;然后使用burpsuite或者yakit去抓包。 一、使用burpsuiteproxifier的抓包测试 1、先配置proxifier&#xff0c;开启http流量转发 勾选确定 2、配置burp对应代理端口&#xff0c;选择profile&#xff0c;点…

基于React和TypeScript的开源白板项目(Github项目分享)

在学习前端开发的过程中&#xff0c;有时候我们需要一些有趣的项目来提升我们的技能。今天我要给大家介绍的是一个非常酷的项目——NinjaSketch&#xff0c;这是一个用React和TypeScript构建的简易白板工具。这个项目使用了Rough.js来实现手绘风格的效果。尽管这个应用不是响应…

vue中如何使用echarts和echarts-gl实现三维折线图和三维柱状图

一、vue中使用三维折线图 效果图&#xff1a; 二、使用步骤 1.引入库 安装echarts 在package.json文件中添加 "dependencies": {"echarts": "^5.1.2""echarts-gl": "^1.1.1",// "echarts-gl": "^2.0.8…

【C++】 解决 C++ 语言报错:Memory Leak

文章目录 引言 内存泄漏&#xff08;Memory Leak&#xff09;是 C 编程中常见且严重的内存管理问题之一。当程序分配了内存而没有正确释放&#xff0c;导致内存无法被重新利用时&#xff0c;就会发生内存泄漏。这种错误会导致程序占用越来越多的内存&#xff0c;最终可能导致系…

Zabbix动作与媒介

目录 前言 1. 动作的基本概念 2. 动作的常见用途 一. 环境准备 二. 创建动作 三. 添加媒介 前言 在 Zabbix 中&#xff0c;动作&#xff08;Actions&#xff09;用于在特定事件发生时执行一系列预定义的操作&#xff0c;比如发送通知、执行脚本等。动作通常与触发器&…