【单调栈】LeetCode2030:含特定字母的最小子序列

2023-12-21 21:20

本文主要是介绍【单调栈】LeetCode2030:含特定字母的最小子序列,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作者推荐

map|动态规划|单调栈|LeetCode975:奇偶跳

涉及知识点

单调栈

题目

给你一个字符串 s ,一个整数 k ,一个字母 letter 以及另一个整数 repetition 。
返回 s 中长度为 k 且 字典序最小 的子序列,该子序列同时应满足字母 letter 出现 至少 repetition 次。生成的测试用例满足 letter 在 s 中出现 至少 repetition 次。
子序列 是由原字符串删除一些(或不删除)字符且不改变剩余字符顺序得到的剩余字符串。
字符串 a 字典序比字符串 b 小的定义为:在 a 和 b 出现不同字符的第一个位置上,字符串 a 的字符在字母表中的顺序早于字符串 b 的字符。
示例 1:
输入:s = “leet”, k = 3, letter = “e”, repetition = 1
输出:“eet”
解释:存在 4 个长度为 3 ,且满足字母 ‘e’ 出现至少 1 次的子序列:

  • “lee”(“leet”)
  • “let”(“leet”)
  • “let”(“leet”)
  • “eet”(“leet”)
    其中字典序最小的子序列是 “eet” 。
    示例 2:
    输入:s = “leetcode”, k = 4, letter = “e”, repetition = 2
    输出:“ecde”
    解释:“ecde” 是长度为 4 且满足字母 “e” 出现至少 2 次的字典序最小的子序列。
    示例 3:
    输入:s = “bb”, k = 2, letter = “b”, repetition = 2
    输出:“bb”
    解释:“bb” 是唯一一个长度为 2 且满足字母 “b” 出现至少 2 次的子序列。
    参数范围
    1 <= repetition <= k <= s.length <= 5 * 104
    s 由小写英文字母组成
    letter 是一个小写英文字母,在 s 中至少出现 repetition 次

单调栈

最简单的情况:不限制长度和重复字符

从左到右将s[i]放到栈中,放入之前向将栈中大于s[i]的出栈。下面来证明:
假定s的最小子系列的为依次为:{i1,i2,i3…}。
规则一:i1之前没有数小于等于它。否则将此数的放在最前会变得最小。
规则二:i1之后没有数小于它,否则替换i1会变得更小。
规则三:i1和i2之间没有数小于等于i2。
规则四:i2之后没有数小于它,否则替换i2会变得更小。

栈底到栈顶就是最小子系列。
i1在栈底,说明它前面的数都大于它,所以出栈了。
i1没有出栈,说明i1之后没有数比它小,否则i1出栈了。
i2之前,i1之后没有数据,说明两者之间没有数小于等于i2,否则不会出栈。
i2没有出栈说明之后没有数据小于它,否则出栈了。

大数出栈后,变成前面的数小,后面的数大。也就是递增的单调栈。

注意: {1,1,1} 前面增加1,变成{1,1,1,1} 有些规则可能变小,有些规则可能变大。

限制长度k

限制了长度,也就限制了出栈次数。前面的数变小比后面的数变小更小,所以把出栈的机会留给前面。
出栈的时候增加判断:如果出栈后,使得长度一定少于k则不出栈。
入栈的时候增加判断:如果长度已经为k,则不入栈。

必须特定个特定字母

如果出栈会造成特定字母不足则不出栈。
入栈时增加处理:如果长度为k,但当前是特定字母,不入栈则特定字母不足,出栈栈顶所有特定字母,直到非特定字母。出栈它,把出栈的特定字母和当前字母入栈。这个操作在超时的边缘。

代码

核心代码

class Solution {
public:string smallestSubsequence(const string s, const int k, const char letter, const int repetition) {int iCanOutCount = std::count(s.begin(), s.end(), letter) - repetition;//可以不使用letter的数量vector<char> sta;for (int i = 0; i < s.length(); i++){const auto& ch = s[i];auto NeedPop = [&](){if (sta.empty() || (sta.size() + s.length() - i <= k) || (sta.back() <= ch)){return false;}if ((sta.back() == letter) && (iCanOutCount <= 0)){return false;}return true;};while (NeedPop()){iCanOutCount -= (sta.back() == letter);sta.pop_back();}if (sta.size() < k){sta.emplace_back(ch);}else if ((letter == ch)&&( iCanOutCount <= 0 )){int iCnt = 1;while (sta.size()+ iCnt > k ){if (letter == sta.back()){iCnt++;}sta.pop_back();}while (iCnt--){sta.emplace_back(letter);}}else{iCanOutCount -= (letter == ch);}}sta.emplace_back(0);return sta.data();}
};

测试用例

template<class T>
void Assert(const T& t1, const T& t2)
{assert(t1 == t2);
}template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{if (v1.size() != v2.size()){assert(false);return;}for (int i = 0; i < v1.size(); i++){Assert(v1[i], v2[i]);}
}int main()
{string s;int k;char letter;int repetition;{Solution slu;s = "leet", k = 3, letter = 'e', repetition = 1;auto res = slu.smallestSubsequence(s, k, letter, repetition);Assert(std::string("eet"), res);}{Solution slu;s = "leetcode", k = 4, letter = 'e', repetition = 2;auto res = slu.smallestSubsequence(s, k, letter, repetition);Assert(std::string("ecde"), res);}{Solution slu;s = "bb", k = 2, letter ='b', repetition = 2;auto res = slu.smallestSubsequence(s, k, letter, repetition);Assert(std::string("bb"), res);}{Solution slu;s = "aaabbbcccddd", k = 3, letter = 'b', repetition = 2;auto res = slu.smallestSubsequence(s, k, letter, repetition);Assert(std::string("abb"), res);}{Solution slu;s = "hjjhhhmhhwhz", k = 6, letter = 'h', repetition = 5;auto res = slu.smallestSubsequence(s, k, letter, repetition);Assert(std::string("hhhhhh"), res);}//CConsole::Out(res);
}

优化

非特定字母最多入栈:k - repetition。注意 :非特定字母入栈的时候,也要判断栈的长度小于k。

template<class T>
void Assert(const T& t1, const T& t2)
{assert(t1 == t2);
}template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{if (v1.size() != v2.size()){assert(false);return;}for (int i = 0; i < v1.size(); i++){Assert(v1[i], v2[i]);}
}int main()
{string s;int k;char letter;int repetition;{Solution slu;s = "leet", k = 3, letter = 'e', repetition = 1;auto res = slu.smallestSubsequence(s, k, letter, repetition);Assert(std::string("eet"), res);}{Solution slu;s = "leetcode", k = 4, letter = 'e', repetition = 2;auto res = slu.smallestSubsequence(s, k, letter, repetition);Assert(std::string("ecde"), res);}{Solution slu;s = "bb", k = 2, letter ='b', repetition = 2;auto res = slu.smallestSubsequence(s, k, letter, repetition);Assert(std::string("bb"), res);}{Solution slu;s = "aaabbbcccddd", k = 3, letter = 'b', repetition = 2;auto res = slu.smallestSubsequence(s, k, letter, repetition);Assert(std::string("abb"), res);}{Solution slu;s = "hjjhhhmhhwhz", k = 6, letter = 'h', repetition = 5;auto res = slu.smallestSubsequence(s, k, letter, repetition);Assert(std::string("hhhhhh"), res);}//CConsole::Out(res);
}

2023年3月旧版

class Solution {
public:
string smallestSubsequence(string s, int k, const char letter, int repetition) {
m_c = s.length();
int iCanNotUseLetterNum = -repetition;//可以不使用letter多少次
int iCanNotUseChar = m_c - k;
vector vAns;
for (const auto& ch : s)
{
if (ch == letter)
{
iCanNotUseLetterNum++;
}
}
for (int i = 0; i < m_c; i++)
{
const auto& ch = s[i];
while (vAns.size() && (ch < vAns.back()) && iCanNotUseChar && ((letter != vAns.back()) || iCanNotUseLetterNum))
{
if (letter == vAns.back())
{
iCanNotUseLetterNum–;
}
iCanNotUseChar–;
vAns.pop_back();
}
vAns.push_back(ch);
}
string strRet;
for (int iIndex = vAns.size() - 1; iIndex >= 0; iIndex–)
{
const char& ch = vAns[iIndex];
if (0 == iCanNotUseChar)
{
strRet += ch;
continue;
}
if (letter == ch )
{
if (iCanNotUseLetterNum)
{
iCanNotUseLetterNum–;
iCanNotUseChar–;
}
else
{
strRet += ch;
}
}
else
{
iCanNotUseChar–;
}
}
vector tmp(strRet.rbegin(), strRet.rend());
tmp.push_back(0);
return tmp.data();
}
int m_c;
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关

下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法C++ 实现。

这篇关于【单调栈】LeetCode2030:含特定字母的最小子序列的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

usaco 1.2 Name That Number(数字字母转化)

巧妙的利用code[b[0]-'A'] 将字符ABC...Z转换为数字 需要注意的是重新开一个数组 c [ ] 存储字符串 应人为的在末尾附上 ‘ \ 0 ’ 详见代码: /*ID: who jayLANG: C++TASK: namenum*/#include<stdio.h>#include<string.h>int main(){FILE *fin = fopen (

uva 10131 最长子序列

题意: 给大象的体重和智商,求体重按从大到小,智商从高到低的最长子序列,并输出路径。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vect

POJ1631最长单调递增子序列

最长单调递增子序列 import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import java.io.PrintWriter;import java.math.BigInteger;import java.util.StringTokenizer;publ

leetcode105 从前序与中序遍历序列构造二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。 注意: 你可以假设树中没有重复的元素。 例如,给出 前序遍历 preorder = [3,9,20,15,7]中序遍历 inorder = [9,3,15,20,7] 返回如下的二叉树: 3/ \9 20/ \15 7   class Solution {public TreeNode buildTree(int[] pr

Android中如何实现adb向应用发送特定指令并接收返回

1 ADB发送命令给应用 1.1 发送自定义广播给系统或应用 adb shell am broadcast 是 Android Debug Bridge (ADB) 中用于向 Android 系统发送广播的命令。通过这个命令,开发者可以发送自定义广播给系统或应用,触发应用中的广播接收器(BroadcastReceiver)。广播机制是 Android 的一种组件通信方式,应用可以监听广播来执行

day-50 求出最长好子序列 I

思路 二维dp,dp[i][h]表示nums[i] 结尾,且有不超过 h 个下标满足条件的最长好子序列的长度(0<=h<=k),二维数组dp初始值全为1 解题过程 状态转换方程: 1.nums[i]==nums[j],dp[i,h]=Math.max(dp[i,h],dp[j,h]+1) 2.nums[i]!=nums[j],dp[i,h]=Math.max(dp[i,h],dp[j,h-1

LeetCode:3177. 求出最长好子序列 II 哈希表+动态规划实现n*k时间复杂度

3177. 求出最长好子序列 II 题目链接 题目描述 给你一个整数数组 nums 和一个非负整数k 。如果一个整数序列 seq 满足在下标范围 [0, seq.length - 2] 中 最多只有 k 个下标i满足 seq[i] != seq[i + 1] ,那么我们称这个整数序列为好序列。请你返回 nums中好子序列的最长长度。 实例1: 输入:nums = [1,2,1,1,3],

用Python实现时间序列模型实战——Day 14: 向量自回归模型 (VAR) 与向量误差修正模型 (VECM)

一、学习内容 1. 向量自回归模型 (VAR) 的基本概念与应用 向量自回归模型 (VAR) 是多元时间序列分析中的一种模型,用于捕捉多个变量之间的相互依赖关系。与单变量自回归模型不同,VAR 模型将多个时间序列作为向量输入,同时对这些变量进行回归分析。 VAR 模型的一般形式为: 其中: ​ 是时间  的变量向量。 是常数向量。​ 是每个时间滞后的回归系数矩阵。​ 是误差项向量,假

时间序列|change point detection

change point detection 被称为变点检测,其基本定义是在一个序列或过程中,当某个统计特性(分布类型、分布参数)在某时间点受系统性因素而非偶然因素影响发生变化,我们就称该时间点为变点。变点识别即利用统计量或统计方法或机器学习方法将该变点位置估计出来。 Change Point Detection的类型 online 指连续观察某一随机过程,监测到变点时停止检验,不运用到

超级 密码加密 解密 源码,支持表情,符号,数字,字母,加密

超级 密码加密 解密 源码,支持表情,符号,数字,字母,加密 可以将表情,动物,水果,表情,手势,猫语,兽语,狗语,爱语,符号,数字,字母,加密和解密 可以将文字、字母、数字、代码、标点符号等内容转换成新的文字形式,通过简单的文字以不同的排列顺序来表达不同的内容 源码截图: https://www.httple.net/152649.html