402 移掉k个数字
结合官方题解以及 https://leetcode-cn.com/problems/create-maximum-number/solution/yi-zhao-chi-bian-li-kou-si-dao-ti-ma-ma-zai-ye-b-7/ 看下面的思路
算法的核心原理是贪心算法,可以思考一下当执行到删k-1步得到最小时值时,如何采取下一步策略能够得到删k的最优解,并利用替换的思想作证明
**具体为:**假设一个最优解删除的元素位置为
p
1
,
p
2
,
.
.
,
p
k
−
1
,
p
k
p_1,p_2,..,p_{k-1},p_k
p1,p2,..,pk−1,pk, 设删掉
p
1
,
p
2
,
.
.
,
p
k
−
1
p_1,p_2,..,p_{k-1}
p1,p2,..,pk−1位置后的序列为x,则下一步a.删除x中第一个不符合升序的元素的前一个位置pos,b.若x中元素均为升序,则最后一个位置作为pos,无论是a还是b,一定是构成最优解的,也就是pos等于
p
k
p_k
pk, 否则以pos替代
p
k
p_k
pk就能得到一个更优解,这和假设中序列
p
1
,
p
2
,
.
.
,
p
k
−
1
,
p
k
p_1,p_2,..,p_{k-1},p_k
p1,p2,..,pk−1,pk构成最优矛盾。
c++代码:
//此算法的核心原理是贪心算法,可以思考一下当执行到删k-1步得到最小时值时,如何采取下一步策略能够得到删k的最优解,并利用替换证明
//贪心策略确实能够构成全局最优解
//先思考num中没有零时的情况
//若存在零,则以同样的逻辑处理,只是在输出时屏蔽前导0,具体可以看看官方的输出示例!
class Solution {
public:
string removeKdigits(string num, int org_k) {
vector<char> st; //利用vector模拟栈,方便取出元素
int k = org_k; //为说明算法才写了这个变量,实际可以不需要
//*************************************************************
//此段为单调栈的写法,但未用到单调栈的性质
//而是利用了贪心策略
for (auto& digit: num) {
while (k>0 && !st.empty() && st.back() > digit) {
st.pop_back();
--k;
}
st.push_back(digit);
}
//*************************************************************
//为何不要担心栈为空还继续进行pop操作?
//原因:
// 由于每个元素都必须入栈,设num中的元素个数为 n ,由题意得 org_k<=n, 即 n - org_k >=0
// 另一方面,出栈次数为 org_k - k,故栈中保留的元素个数为 n - (org_k - k) = n-org_k+k >= k
// 因此不用担心会出现栈为空还继续进行pop操作
for (; k > 0; --k) {
st.pop_back();
}
//至此,栈中的元素一定就是我们需要的答案
//但是结合输出示例可知,前导0无需显示,因此要跳过
//下面分三种情况讨论看一下的输出代码:
// 1. st 为空,即意味着org_k 等于 num.size(),依照题例,直接输出 "0"
// 2. 存在前导0则直接跳过,若跳过后栈为空,输出的也是 "0" 若跳过后非空,则直接输出非空的结果
// 3. 不存在前导0,则直接输出栈中元素作为结果
string ans = "";
bool isLeadingZero = true;
for (auto& digit: st) {
if (isLeadingZero && digit == '0') {
continue;
}
isLeadingZero = false;
ans += digit;//这行代码表示的是字符串的拼接,不要理解成值的相加
}
return ans == "" ? "0" : ans;
}
};
316
//理解402再做此题
class Solution {
public:
string removeDuplicateLetters(string s) {
unordered_map<char,int> count;
vector<bool> is_in_st(26,false);
//统计每个字母出现的次数
for(auto & ch : s){
++count[ch];
}
//下面代码与402十分类似
//计算重复元素的个数,即需要移除的元素个数
// int k = s.size() - count.size(); //测试用
string st;
for(auto & ch : s){
if(is_in_st[ch-'a'] == false){
while(!st.empty() && st.back() > ch && count[ st.back() ] > 1){
--count[ st.back() ]; //不要漏掉这个!
// --k; //测试用
is_in_st[st.back()-'a'] = false;
st.pop_back();
}
st.push_back(ch);
is_in_st[ch-'a'] = true;
}else{
--count[ ch ];
// --k; //测试用
}
}
// // 测试用
// string ans1(st.begin(),st.end());
// cout<<ans1<<k;
return st;
}
};