动态规划算法专题(四):子串、子数组系列

news2024/12/28 22:16:18

目录

1、最大子数组和

1.1 算法原理

1.2 算法代码

2、环形子数组的最大和

2.1 算法原理

2.2 算法代码

3、乘积最大子数组

3.1 算法原理

3.2 算法代码

4、乘积为正数的最长子数组长度

4.1 算法原理

4.2 算法代码

5、等差数列划分

5.1 算法原理 

5.2 算法代码

6、最长湍流子数组

6.1 算法原理 

6.2 算法代码

7、单词拆分

7.1 算法原理

7.2 算法代码

8、环绕字符串中唯一的子字符串

8.1 算法原理 

 8.2 算法代码


1、最大子数组和

. - 力扣(LeetCode)

1.1 算法原理

  • 状态表示dp[i]:

以i位置为结尾,最长子序列之和

  • 状态转移方程:

dp[i]=max(dp[i-1]+nums[i-1],nums[i-1]);

  • 初始化:

int[] dp = new int[n+1];
dp[0]=0;

  • 填表顺序:

从左往右

  • 返回值:

dp中最大序列和

1.2 算法代码

class Solution {
    public int maxSubArray(int[] nums) {
        int n = nums.length;
        int[] dp = new int[n + 1];
        // 初始化
        dp[0] = 0;
        int ret = Integer.MIN_VALUE;
        // 填表
        for(int i = 1; i <= n; i++) {
            dp[i] = Math.max(dp[i - 1] + nums[i - 1], nums[i - 1]);
            ret = Math.max(ret, dp[i]);
        }
        return ret;
    }
}

2、环形子数组的最大和

. - 力扣(LeetCode)

2.1 算法原理

核心:将带环问题转化为普通不带环问题

  • 状态表示:

f[i]:以i位置为结尾,最大子序列之和
g[i]:以i位置为结尾,最小子序列之和

  • 状态转移方程:

f[i] = Math.max(f[i - 1] + nums[i - 1], nums[i - 1]);

g[i] = Math.min(g[i - 1] + nums[i - 1], nums[i - 1]);

  • 初始化:

下标映射:dp[i]-->nums[i-1]
虚拟节点:要保证后续填表的正确性:f[0]=g[0]=0;

  • 填表顺序:

从左往右,两个表一起填。

  • 返回值(特殊情况:当表中的数都是负数时(都在最小子序列中),此时sum-gMin=0):

找到f表中的最大值 fMax;
找到g表中的最小值 gMin,进而得到最大和序列:sum-gMin;
return sum == gMin ? fMax : max(fMax, sum-gMin);

2.2 算法代码

class Solution {
    public int maxSubarraySumCircular(int[] nums) {
        int n = nums.length;
        int sum = 0;
        for(int x : nums) sum += x;
        int[] f = new int[n + 1];//内部最大子数组
        int[] g = new int[n + 1];//内部最小子数组
        f[0] = g[0] = 0;
        int fMax = Integer.MIN_VALUE;
        int gMin = Integer.MAX_VALUE;
        for(int i = 1; i <= n; i++) {
            f[i] = Math.max(f[i - 1] + nums[i - 1], nums[i - 1]);
            g[i] = Math.min(g[i - 1] + nums[i - 1], nums[i - 1]);
            fMax = Math.max(fMax, f[i]);
            gMin = Math.min(gMin, g[i]);
        }
        // 注意数组中全是负数的情况
        return sum == gMin ? fMax : Math.max(fMax, sum - gMin);
    }
}

3、乘积最大子数组

. - 力扣(LeetCode)

3.1 算法原理

  • 状态表示:

f[i]:以i位置为结尾,最大乘积
g[i]:以i位置为结尾,最小乘积

  • 状态转移方程:

f[i]=max(nums[i-1], f[i-1]*nums[i-1], g[i-1]*nums[i-1]);
g[i]=min(nums[i-1], f[i-1]*nums[i-1], g[i-1]*nums[i-1]);

  • 初始化:

f[1]=g[1]=1;

  • 建表顺序:

从左往右,两个表一起填

  • 返回值:

f表中的最大值

3.2 算法代码

class Solution2 {
    public int maxProduct(int[] nums) {
        int n = nums.length;
        int[] f = new int[n + 1];// 以i位置为结尾,最大乘积
        int[] g = new int[n + 1];// 以i位置为结尾,最小乘积
        int ret = Integer.MIN_VALUE;
        // 初始化
        f[0] = g[0] = 1;
        // 建表
        for(int i = 1; i <= n; i++) {
            f[i] = Math.max(nums[i - 1], Math.max(nums[i - 1] * f[i - 1], nums[i - 1] * g[i - 1]));
            g[i] = Math.min(nums[i - 1], Math.min(nums[i - 1] * f[i - 1], nums[i - 1] * g[i - 1]));
            ret = Math.max(ret, f[i]);
        }
        return ret;
    }
}

4、乘积为正数的最长子数组长度

. - 力扣(LeetCode)

4.1 算法原理

  • 状态表示:

f[i]:以i位置为结尾,乘积为 正数 的,最长子序列的长度。

g[i]:以i位置为结尾,乘积为 负数 的,最长子序列的长度。

  • 状态转移方程:

①:nums[i] > 0

f[i] = f[i-1]+1; g[i] = (g[i-1]==0 ? 0 : g[i-1]+1);

②:nums[i] < 0

f[i] = (g[i-1]==0 ? 0 : g[i-1]+1); g[i] = f[i-1]+1;

  • 初始化:

①:f[0]=g[0]=0; // 虚拟节点的值,不影响后续填表的正确性

②:下标映射关系

  • 返回值:

f表中的最大值

4.2 算法代码

class Solution {
    public int getMaxLen(int[] nums) {
        int n = nums.length;
        int[] f = new int[n + 1];// 乘积为正数的最长序列的长度
        int[] g = new int[n + 1];// 乘积为负数的最长序列的长度
        int ret = 0;
        // 填表
        for(int i = 1; i <= n; i++) {
            if(nums[i - 1] > 0) {
                f[i] = f[i - 1] + 1;
                g[i] = (g[i - 1] == 0 ? 0 : g[i - 1] + 1);
            }
            if(nums[i - 1] < 0) {
                f[i] = (g[i - 1] == 0 ? 0 : g[i - 1] + 1);
                g[i] = f[i - 1] + 1;
            }
            ret = Math.max(ret, f[i]);
        }
        return ret;
    }
}

5、等差数列划分

. - 力扣(LeetCode)

5.1 算法原理 

  • 状态表示dp[i]:

以i位置为结尾的所有子序列中,等差序列(长度>=3)的数量

  • 状态转移方程:

①:nums[i]-nums[i-1] == nums[i-1]-nums[i-2] => dp[i]=dp[i-1]+1;

②:nums[i]-nums[i-1] != nums[i-1]-nums[i-2] => dp[i]=0;

  • 初始化:

dp[0]=dp[1]=0;(子序列长度不足3)

  • 返回值:

dp表之和

5.2 算法代码

class Solution {
    public int numberOfArithmeticSlices(int[] nums) {
        int n = nums.length;
        int[] dp = new int[n];
        int ret = 0;
        // 填表
        for(int i = 2; i < n; i++) {
            if(nums[i] - nums[i - 1] == nums[i - 1] - nums[i - 2]) {
                dp[i] = dp[i - 1] + 1;
                ret += dp[i];
            }
        }
        return ret;
    }
}

6、最长湍流子数组

. - 力扣(LeetCode)

6.1 算法原理 

  • 状态表示:

f[i]:以i位置为结尾,最后呈现"上升趋势"的最长子序列的长度
g[i]:以i位置为结尾,最后呈现"下降趋势"的最长子序列的长度

  • 状态转移方程:

if(nums[i] > nums[i-1]) f[i]=g[i-1]+1;
if((nums[i] < nums[i-1]) g[i]=f[i-1]+1;

  • 初始化:

f表,g表中所有元素的值初始化为 1 
(最差情况序列长度为1)

  • 建表顺序:

从左往右

  • 返回值:

f 和 g 表中的最大值

6.2 算法代码

class Solution {
    public int maxTurbulenceSize(int[] arr) {
        int n = arr.length;
        int[] f = new int[n];// f[i]: 最后呈现"上升"趋势的最长湍流子数组的长度
        int[] g = new int[n];// g[i]: 最后呈现"下降"趋势的最长湍流子数组的长度
        // 初始化
        Arrays.fill(f, 1);
        Arrays.fill(g, 1);
        int ret = 1;
        // 建表 + 处理返回值
        for(int i = 1; i < n; i++) {
            if(arr[i] > arr[i - 1]) f[i] = g[i - 1] + 1;
            if(arr[i] < arr[i - 1]) g[i] = f[i - 1] + 1;
            ret = Math.max(ret, Math.max(f[i], g[i]));
        }
        return ret;
    }
}

7、单词拆分

. - 力扣(LeetCode)

7.1 算法原理

  • 状态表示dp[i]:

[0, i]区间的字符串,能否被字典中的单词拼接而成

  • 状态转移方程:

根据最后一个单词的位置,划分问题。
设j为最后一个单词的起始位置(0<=j<=i),
判断条件①:[j, i]区间的字符串是否存在于字典中
判断条件②:[0, j-1]区间的字符串是否可被拼接而成

  • 初始化:

 dp[0]=true;(里面的值要保证后续的填表是正确的)
 优化:s = s+ " ";(下标映射的处理)

  • 填表顺序:

从左往右

  • 返回值:

dp[n];

7.2 算法代码

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        // 优化一:哈希
        Set<String> hash = new HashSet<>(wordDict);
        int n = s.length();
        boolean[] dp = new boolean[n + 1];
        s = " " + s;
        // 初始化
        dp[0] = true;
        for(int i = 1; i <= n; i++) {
            for(int j = i; j >= 1; j--) {
                if(hash.contains(s.substring(j, i + 1)) && dp[j - 1]) {
                    dp[i] = true;
                    // 优化二
                    break;
                }
            }
        }
        return dp[n];
    }
}

8、环绕字符串中唯一的子字符串

. - 力扣(LeetCode)

8.1 算法原理 

  • 状态表示:

以i位置字符为结尾的所有不同的子串,有多少个在base中出现过

  • 状态转移方程:

if(s[i-1]+1 == s[i] || (s[i-1] == 'z' && s[i] == 'a')) --> dp[i] += dp[i-1];

  • 初始化:

Arrays.fill(dp, 1);// 最差情况下,字符本身构成base中的子串

  • 填表顺序:

从左往右

  • 返回值:

给重复的子串去重:
相同字符结尾的dp值,取最大的即可(虽然是相同字符结尾的子串,但dp值更大的这种情况,
包含了dp值小的情况):
1. int[26] hash // 存相应字符结尾的最大的dp值
2. 返回hash数组的和

 8.2 算法代码

class Solution {
    public int findSubstringInWraproundString(String ss) {
        char[] s = ss.toCharArray();
        int n = s.length;
        // 状态表示:以i位置字符为结尾的所有不同的子串,有多少个在base中出现过
        int[] dp = new int[n];
        int[] hash = new int[26];
        Arrays.fill(dp, 1);
        // 填表
        for(int i = 1; i < n; i++) {
            if(s[i - 1] + 1 == s[i] || (s[i - 1] == 'z' && s[i] == 'a')) {
                dp[i] += dp[i - 1];
            }
        }
        // 去重
        for(int i = 0; i < n; i++) {
            int index = s[i] - 'a';
            hash[index] = Math.max(hash[index], dp[i]);
        }
        // 返回结果
        int ret = 0;
        for(int x : hash) ret += x;
        return ret; 
    }
}

END

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++读取大文件三种方法速度比较

目录 测试说明第一种方法&#xff1a;按块读&#xff0c;一次读8kb第二种方法&#xff1a;按行读&#xff0c;一次读一行第三种方法&#xff1a;多线程并行读取完整示例 测试说明 测试文件&#xff1a;100万行&#xff0c;每一行是两个小数&#xff0c;中间用逗号隔开&#xf…

python脚本实现Redis未授权访问漏洞利用

之前介绍过Redis未授权访问漏洞&#xff0c;本文使用python实现Redis未授权访问检测以及对应三种getshell。 1 测试环境准备 CentOS 7&#xff08;192.168.198.66/24&#xff09;&#xff1a;安装 Redis 服务器并用 root 权限开启服务&#xff0c;关闭保护模式&#xff1b;安…

4-coumarate--CoA ligase4-香豆酸:辅酶A连接酶4CL-文献精读63

Characterization and Functional Analysis of 4-Coumarate:CoA Ligase Genes in Mulberry 桑树中4-香豆酸&#xff1a;辅酶A连接酶基因的表征与功能分析 桑树T2T基因组-文献精读16 摘要 4-香豆酸&#xff1a;辅酶A连接酶&#xff08;4CL&#xff09;由一个小型的多基因家族…

pytest(六)——allure-pytest的基础使用

前言 一、allure-pytest的基础使用 二、需要掌握的allure特性 2.1 Allure报告结构 2.2 Environment 2.3 Categories 2.4 Flaky test 三、allure的特性&#xff0c;allure.step()、allure.attach的详细使用 3.1 allure.step 3.2 allure.attach&#xff08;挺有用的&a…

软件测试比赛-学习

一、环境配置 二、浏览器适配 //1.设置浏览器的位置,google浏览器位置是默认且固定在电脑里的//2.设置浏览器驱动的位置,C:\Users\27743\AppData\Local\Google\Chrome\ApplicationSystem.setProperty("webdriver.chrome.driver", "C:\\Users\\27743\\AppData\\…

【python实操】python小程序之对象的属性操作

引言 python小程序之对象的属性操作 文章目录 引言一、对象的属性操作1.1 题目1.2 代码1.3 代码解释 二、思考2.1 添加属性2.2 获取属性 一、对象的属性操作 1.1 题目 给对象添加属性 1.2 代码 class Cat:# 在缩进中书写⽅法def eat(self):# self 会⾃动出现,暂不管print(f…

弹性分布式数据集RDD详细说明

文章目录 整体介绍一、定义与特性二、操作与转换三、存储级别与持久化四、依赖关系与容错机制五、优化与性能调优 常见操作支持的数据格式1.文本文件 (Text Files)2. CSV 文件3. JSON 文件4. Parquet 文件5. Sequence Files6.Hadoop文件读取A. 读取HDFS上的文本文件B. 使用Hado…

(Linux驱动学习 - 8).信号异步通知

一.异步通知简介 1.信号简介 信号类似于我们硬件上使用的“中断”&#xff0c;只不过信号是软件层次上的。算是在软件层次上对中断的一种模拟&#xff0c;驱动可以通过主动向应用程序发送信号的方式来报告自己可以访问了&#xff0c;应用程序获取到信号以后就可以从驱动设备中…

论文阅读——联邦忘却学习研究综述

文章基本信息 作者&#xff1a; 王鹏飞魏宗正周东生宋威肖蕴明孙庚于硕张强 机构&#xff1a; 大连理工大学计算机科学与技术学院大连理工大学社会计算与认知智能教育部重点实验室大连大学先进设计与智能计算教育部重点实验室美国西北大学计算机科学系吉林大学计算机科学与…

QT调用libusb库stm32407上下位机

安富莱USB上位机教程 参考安富莱的视频&#xff0c;不过这里 调用是libusb最新的库 可以参考上一个文章&#xff1a; QT调用最新的libusb库 https://editor.csdn.net/md/?articleId142733711 调试结果&#xff1a; 资源地址&#xff1a; 上位机&#xff1a;https://downl…

基于pytorch的手写数字识别-训练+使用

import pandas as pd import numpy as np import torch import matplotlib import matplotlib.pyplot as plt from torch.utils.data import TensorDataset, DataLoadermatplotlib.use(tkAgg)# 设置图形配置 config {"font.family": serif,"mathtext.fontset&q…

Maven 高级之分模块设计与继承、聚合

在软件开发中&#xff0c;随着项目规模的扩大&#xff0c;代码量和复杂度不断增加&#xff0c;传统的一体化开发模式逐渐暴露出诸多问题。为了解决这些问题&#xff0c;模块化开发应运而生&#xff0c;而 Maven 正是模块化开发的利器&#xff0c;它提供的继承和聚合机制为构建和…

fiddler抓包20_弱网模拟

课程大纲 ① 打开CustomRules.js文件&#xff1a;Fiddler快捷键“CtrlR”(或鼠标点击&#xff0c;菜单栏 - Rules - Customize Rules)。 ② 设置速率&#xff1a;“CtrlF”&#xff0c;搜索 “m_SimulateModem”&#xff0c;定位至函数。在函数里设置上传、下载速率&#xff0c…

ESP8266模块(WIFI STM32)

目录 一、介绍 二、传感器原理 1.原理图 2.引脚描述 3.ESP8266基础AT指令介绍 4.ESP8266基础工作模式 三、程序设计 main.c文件 esp8266.h文件 esp8266.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 ESP8266是一款嵌入式系统级芯片&#xff0c;它集成了Wi…

将自己写好的项目部署在自己的云服务器上

准备工作 这里呢我要下载的终端软件是Xshell 如图&#xff1a; 自己准备好服务器&#xff0c;我这里的是阿里云的服务器&#xff0c; 如图&#xff1a; 这两个准备好之后呢&#xff0c;然后对我们的项目进行打包。 如图&#xff1a; 这里双击打包就行了。 找到自己打成jar包…

零基础多图详解图神经网络(GNN/GCN)【李沐论文精读】

A Gentle Introduction to Graph Neural Networks 在上图中&#xff0c;每一层都是一层网络&#xff0c;每一层的节点都受到下一层中自身节点和邻居节点的影响。如果网络比较深&#xff0c;是可以处理到一幅图中较大范围的节点。 前言 图神经网络在应用上还只是起步阶段&…

基于SpringBoot健身房管理系统【附源码】

效果如下&#xff1a; 系统首页界面 系统注册详细页面 健身课程详细页面 后台登录界面 管理员主页面 员工界面 健身教练界面 员工主页面 健身教练主页面 研究背景 随着生活水平的提高和健康意识的增强&#xff0c;现代人越来越注重健身。健身房作为一种专业的健身场所&#x…

日期类的实现(C++)

个人主页&#xff1a;Jason_from_China-CSDN博客 所属栏目&#xff1a;C系统性学习_Jason_from_China的博客-CSDN博客 所属栏目&#xff1a;C知识点的补充_Jason_from_China的博客-CSDN博客 前言 日期类是六个成员函数学习的总结和拓展&#xff0c;是实践的体现 创建文件 构造函…

HCIP--以太网交换安全(二)

端口安全 一、端口安全概述 1.1、端口安全概述&#xff1a;端口安全是一种网络设备防护措施&#xff0c;通过将接口学习的MAC地址设为安全地址防止非法用户通信。 1.2、端口安全原理&#xff1a; 类型 定义 特点 安全动态MAC地址 使能端口而未是能Stichy MAC功能是转换的…

在VMware WorkStation上安装飞牛OS(NAS系统)

对于NAS系统&#xff0c;小白相信很多小伙伴都不陌生&#xff0c;在许多场景下也能看得到&#xff0c;它其实可以算是文件存储服务器&#xff0c;当然&#xff0c;你如果给它加上其他服务的话&#xff0c;它也能变成网页服务器、Office协同办公服务器等等。 有许多小伙伴都拿这…