【七十一】【算法分析与设计】467. 环绕字符串中唯一的子字符串,940. 不同的子序列 II,子串的划分,子序列的划分,区间划分---递推

本文主要是介绍【七十一】【算法分析与设计】467. 环绕字符串中唯一的子字符串,940. 不同的子序列 II,子串的划分,子序列的划分,区间划分---递推,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

467. 环绕字符串中唯一的子字符串

定义字符串 base 为一个 "abcdefghijklmnopqrstuvwxyz" 无限环绕的字符串,所以 base 看起来是这样的:

  • "...zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd....".

给你一个字符串 s ,请你统计并返回 s 中有多少 不同非空子串 也在 base 中出现。

示例 1:

输入:s = "a" 输出:1 解释:字符串 s 的子字符串 "a" 在 base 中出现。

示例 2:

输入:s = "cac" 输出:2 解释:字符串 s 有两个子字符串 ("a", "c") 在 base 中出现。

示例 3:

输入:s = "zab" 输出:6 解释:字符串 s 有六个子字符串 ("z", "a", "b", "za", "ab", and "zab") 在 base 中出现。

提示:

  • 1 <= s.length <= 10(5)

  • s 由小写英文字母组成

1.

子串的划分,以某个位置结尾的子串,进行子串的划分.

把所有位置结尾的子串中出现在base个数记录下来即可.

2.

只需要记录以某位置结尾往前最长的连续子串长度即可.

例如abcde,bcde,cde,de,e,最长的连续子串长度是5,依次对应五个数量.

3.

子串的划分----分治

 
#include <bits/stdc++.h>  // 引入常用的头文件
using namespace std;class Solution {
public:using ll = long long;  // 定义类型别名ll,表示长整型long longstring s;              // 定义字符串s,存储输入字符串vector<ll> dp;         // 使用动态规划存储中间结果,dp[i]存储以s[i]结尾的子串的合法子串数量vector<ll> count;      // 记录每个字母为结束字符的最长子串长度// 初始化函数,清空并重设dp和count数组void solveinit() {dp.clear();                     // 清空dp数组dp.resize(s.size(), -1);        // 重设dp数组大小,初始值为-1count.clear();                  // 清空count数组count.resize(26);               // 重设count数组大小,大小为26(26个字母)}// 深度优先搜索函数,用于计算以第i个字符结束的子串的合法子串数量ll dfs(ll i) {if (dp[i] != -1)                // 如果dp[i]已计算过,直接返回结果return dp[i];if (i - 1 >= 0) {               // 如果不是第一个字符,可以考虑前一个字符if (s[i] != 'a') {          // 当前字符不是'a'时if (s[i - 1] == s[i] - 1) {  // 如果前一个字符正好是当前字符的前一个字母dp[i] = dfs(i - 1) + 1;  // 递归计算前一个字符,并加上当前字符形成的新的子串} else {dp[i] = 1;          // 否则,当前字符自成一组}} else {if (s[i - 1] == 'z') {  // 当前字符是'a'且前一个字符是'z'dp[i] = dfs(i - 1) + 1;  // 递归计算前一个字符,并加上当前字符形成的新的子串} else {dp[i] = 1;          // 否则,当前字符自成一组}}} else {dp[i] = 1;                  // 如果是第一个字符,只有当前字符自身一个子串}return dp[i];                   // 返回以s[i]结尾的合法子串数量}// 主函数,计算字符串s中有多少不同非空子串也在环绕字符串base中出现int findSubstringInWraproundString(string _s) {s = _s;                         // 存储输入字符串solveinit();                    // 初始化dp和count数组for (ll i = 0; i < s.size(); i++) {dfs(i);                     // 对每个字符调用dfs函数,计算以每个字符结尾的子串数量}ll ans = 0;vector<bool> visited(26);       // 标记数组,记录每个字符是否被访问过for (ll i = 0; i < s.size(); i++) {int index = s[i] - 'a';     // 当前字符对应的索引count[index] = max(count[index], dp[i]);  // 更新以当前字符结尾的最大子串数量}for (ll i = 0; i < 26; i++) {ans += count[i];            // 累加每个字母对应的最长子串数量}return ans;                     // 返回结果}
};

940. 不同的子序列 II

给定一个字符串 s,计算 s不同非空子序列 的个数。因为结果可能很大,所以返回答案需要对 10^9 + 7 取余

字符串的 子序列 是经由原字符串删除一些(也可能不删除)字符但不改变剩余字符相对位置的一个新字符串。

  • 例如,"ace""abcde" 的一个子序列,但 "aec" 不是。

示例 1:

输入:s = "abc" 输出:7 解释:7 个不同的子序列分别是 "a", "b", "c", "ab", "ac", "bc", 以及 "abc"。

示例 2:

输入:s = "aba" 输出:6 解释:6 个不同的子序列分别是 "a", "b", "ab", "ba", "aa" 以及 "aba"。

示例 3:

输入:s = "aaa" 输出:3 解释:3 个不同的子序列分别是 "a", "aa" 以及 "aaa"。

提示:

  • 1 <= s.length <= 2000

  • s 仅由小写英文字母组成

1.

假设s长度为n.

区间[0,n-1]的字符串的不同的子序列个数是多少.

区间[0,n-2]的字符串的不同的子序列个数是多少.

可以选择由[0,n-2]区间字符串的不同子序列个数多少推导出[0,n-1]的字符串的不同的子序列个数是多少.

因为新增的子序列是[0,n-2]区间所有子序列后面全部加上n-1位置的字符所组成的子序列加上n-1位置单独的字符子序列.

只需要完成新增子序列与老子序列的去重操作即可.

新增子序列都是以n-1位置结尾的子序列,一定是包含了所有的以n-1字符结尾的可能性.

所以老子序列中去掉以n-1字符结尾的可能性加上其他字符结尾的个数即可.

2.

子序列的划分----分治

长度的划分,递推,增加的子序列很好计算.

 
class Solution {
public:using ll = long long; // 定义类型别名 ll,表示长整型 long longstring s; // 用于存储输入的字符串vector<ll> count; // 使用一个数组来统计每个字符作为子序列结尾的不同子序列个数const ll MOD = 1e9 + 7; // 定义常量 MOD,取值为 10^9 + 7,用于结果的模运算void solveinit() {count.clear(); // 清空 count 数组count.resize(26); // 将 count 数组大小重新设置为26(对应26个英文字母)}int distinctSubseqII(string _s) {s = _s; // 将传入的字符串赋值给成员变量 ssolveinit(); // 调用初始化函数,准备 count 数组for (ll i = 0; i < s.size(); i++) { // 遍历字符串 s 的每个字符ll all = 0; // 初始化 all 变量,用来统计到当前字符为止的所有可能子序列的数量for (ll j = 0; j < count.size(); j++) { // 遍历 count 数组,累加之前所有字符的子序列总数all = (all + count[j]) % MOD; // 将累加结果进行模运算,防止溢出}ll index = s[i] - 'a'; // 计算当前字符在 count 数组中的索引位置count[index] = (all + 1) % MOD; // 更新当前字符的子序列数。加1是因为当前字符本身也是一个子序列}ll ans = 0; // 初始化答案变量,用来存储最终的不同子序列总数for (ll i = 0; i < count.size(); i++) { // 遍历 count 数组,将所有字符的子序列数累加ans = (ans + count[i]) % MOD; // 累加的同时进行模运算,防止溢出}return ans; // 返回最终计算的结果}
};

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

这篇关于【七十一】【算法分析与设计】467. 环绕字符串中唯一的子字符串,940. 不同的子序列 II,子串的划分,子序列的划分,区间划分---递推的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/943715

相关文章

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

SpringBoot实现不同接口指定上传文件大小的具体步骤

《SpringBoot实现不同接口指定上传文件大小的具体步骤》:本文主要介绍在SpringBoot中通过自定义注解、AOP拦截和配置文件实现不同接口上传文件大小限制的方法,强调需设置全局阈值远大于... 目录一  springboot实现不同接口指定文件大小1.1 思路说明1.2 工程启动说明二 具体实施2

如何通过try-catch判断数据库唯一键字段是否重复

《如何通过try-catch判断数据库唯一键字段是否重复》在MyBatis+MySQL中,通过try-catch捕获唯一约束异常可避免重复数据查询,优点是减少数据库交互、提升并发安全,缺点是异常处理开... 目录1、原理2、怎么理解“异常走的是数据库错误路径,开销比普通逻辑分支稍高”?1. 普通逻辑分支 v

C# LiteDB处理时间序列数据的高性能解决方案

《C#LiteDB处理时间序列数据的高性能解决方案》LiteDB作为.NET生态下的轻量级嵌入式NoSQL数据库,一直是时间序列处理的优选方案,本文将为大家大家简单介绍一下LiteDB处理时间序列数... 目录为什么选择LiteDB处理时间序列数据第一章:LiteDB时间序列数据模型设计1.1 核心设计原则

Java使用正则提取字符串中的内容的详细步骤

《Java使用正则提取字符串中的内容的详细步骤》:本文主要介绍Java中使用正则表达式提取字符串内容的方法,通过Pattern和Matcher类实现,涵盖编译正则、查找匹配、分组捕获、数字与邮箱提... 目录1. 基础流程2. 关键方法说明3. 常见场景示例场景1:提取所有数字场景2:提取邮箱地址4. 高级

Python Flask实现定时任务的不同方法详解

《PythonFlask实现定时任务的不同方法详解》在Flask中实现定时任务,最常用的方法是使用APScheduler库,本文将提供一个完整的解决方案,有需要的小伙伴可以跟随小编一起学习一下... 目录完js整实现方案代码解释1. 依赖安装2. 核心组件3. 任务类型4. 任务管理5. 持久化存储生产环境

Python 字符串裁切与提取全面且实用的解决方案

《Python字符串裁切与提取全面且实用的解决方案》本文梳理了Python字符串处理方法,涵盖基础切片、split/partition分割、正则匹配及结构化数据解析(如BeautifulSoup、j... 目录python 字符串裁切与提取的完整指南 基础切片方法1. 使用切片操作符[start:end]2

MyBatis的xml中字符串类型判空与非字符串类型判空处理方式(最新整理)

《MyBatis的xml中字符串类型判空与非字符串类型判空处理方式(最新整理)》本文给大家介绍MyBatis的xml中字符串类型判空与非字符串类型判空处理方式,本文给大家介绍的非常详细,对大家的学习或... 目录完整 Hutool 写法版本对比优化为什么status变成Long?为什么 price 没事?怎

Android 缓存日志Logcat导出与分析最佳实践

《Android缓存日志Logcat导出与分析最佳实践》本文全面介绍AndroidLogcat缓存日志的导出与分析方法,涵盖按进程、缓冲区类型及日志级别过滤,自动化工具使用,常见问题解决方案和最佳实... 目录android 缓存日志(Logcat)导出与分析全攻略为什么要导出缓存日志?按需过滤导出1. 按

Linux中的自定义协议+序列反序列化用法

《Linux中的自定义协议+序列反序列化用法》文章探讨网络程序在应用层的实现,涉及TCP协议的数据传输机制、结构化数据的序列化与反序列化方法,以及通过JSON和自定义协议构建网络计算器的思路,强调分层... 目录一,再次理解协议二,序列化和反序列化三,实现网络计算器3.1 日志文件3.2Socket.hpp