题意:
给定一个以字符串表示的数字 num 和一个数字 k ,从 num 中移除 k 位数字,使得剩下的数字最小。如果可以删除全部数字则剩下 0
1.num仅有数字组成
2.num是合法的数字,不含前导0
3.请你保证删除之后的num也不含前导0
方法一:
单调栈
思路:核心:以某字符为中心,寻找左边的最近比该字符严格小的字符。(单调栈:严格单调递增)
步骤:
从左到右遍历字符串,如果栈非空&&栈顶元素>=当前字符&&k!=0,则弹栈,并最后将当前字符入栈。
当k==0时,则说明弹栈次数用完了。
当弹出0的时候,不需k--,因为前导0忽略。
最后,当弹栈次数用完后,只需将栈中字符和未入栈字符拼接,即可得到数值最小化。
class Solution { public: string removeKnums(string num, int k) { stack<char> st; int len=num.size(); int i=0; for(;i<len;i++){//遍历字符串 while(!st.empty()&&st.top()>=num[i]&&k){//保持严格递增的栈 if(st.top()!='0') k--; st.pop(); } if(k==0){ break; } st.push(num[i]); } string res=""; while(!st.empty()){//取出栈中的字符 res=st.top()+res; st.pop(); } res+=num.substr(i);//拼接未入栈的字符 int j=0; while(j<res.size()-1){//去掉前导0 if(res[j]=='0') j++; else break; } return res.substr(j); } };
时间复杂度:空间复杂度:
方法二:
贪心
思路:贪心。
核心:以某字符为中心,寻找左边的最近比该字符严格小的字符。
外层循环以每个字符为中心,内层循环将左边>=该字符的字符全部删除。(这里用vis[ ] 数组的0表示某字符未删除,1表示某字符被删除)
有k次删除机会。
最后,将未被删除的字符拼接即可。
class Solution { public: int vis[100005]={0}; string removeKnums(string num, int k) { int len=num.size(); if(k>=len) return "0"; for(int i=1;i<len;i++){ for(int j=i-1;j>=0;j--){ if(num[j]=='0') continue; if(num[j]>=num[i]&&vis[j]==0){//左边的vis[]值为0且>=当前字符的字符,将vis[]置为1且k-- vis[j]=1; k--; if(k==0) break; } } if(k==0) break; } string res=""; int flag=0; for(int i=0;i<len;i++){ if(num[i]=='0'&&flag==0)//删除前导0 continue; if(vis[i]==0){//拼接vis[]值为0的字符 res+=num[i]; flag=1; } } if(res=="") res="0"; return res; } };
时间复杂度:空间复杂度: