卡了一段时间 思路其实很简单 就是细节不容易考虑全 不翻题解的话 会做算我输
贪心没有特地去考虑 就是第一反应有点单调栈的味道
单调栈处理字符串的题 好像有点固定的套路
推一篇举一反三的题解 不过我没细看 里面有几道类似的题 一招吃遍力扣四道题
回归正题
要求删掉几个数字后 所得数最小
那么很容易想到 越靠前的数字应当越小 越靠后的数字越大 (单调了)
数字依次入栈 保证本次压入栈的数字是目前栈中最大的数(之一)
也就是说 对于每一个新的数字 如果栈顶元素大于它 那么元素出栈 直到栈顶元素小于或等于当前数字 再把当前数字压栈
出栈意味着丢弃该数字 但不能无限制的丢弃 题干要求“移除$k$个” 不能多也不能少 所以每出栈一个元素 k--
表示能被移除的个数减少1
当被移除的元素个数已经达到$k$了 即k == 0
后续就不能再丢弃数字 新数字直接入栈
还要考虑另外一种情况 遍历完后$k$未减少到0
如果原字符串本身已经是最理想的单调(如"12345678"
) 那么遍历过程不会发生移除操作 即最终一个字符都没有移除
未符合题意 要补足$k$个 显然直接移除最后的$k$个就行
如果原字符串是在遍历和移除的过程中达到了理想状态 同样会导致最终$k > 0$
如【1324】要求移除2个数字 那么遍历过程只会移除【3】变为【124】 因此遍历完k==1
还需再移除最后的1个元素 得【12】
上述处理本质上是相同的 都是遍历完$k>0$时再移除最后$k$个数字 代码实现可以合并在一起
最后还要过滤掉前导零的情况
依次出栈 逆序组成最终的字符串 再移除前几个0
要考虑最终数字本身是0的情况 移除完前几个0后 如果字符串长度为0 则最终结果必然为0
附一个在力扣上的初版代码 速度慢 胜在过程清晰
class Solution {
public String removeKdigits(String num, int k) {
ArrayDeque<Character> ad = new ArrayDeque<>();
int n = num.length();
for (int i = 0; i < n; i++) {
char ch = num.charAt(i);
while (!ad.isEmpty() && ad.peek() > ch && k > 0) {
ad.pop(); // 出栈
k--; // 可移除的个数-1
}
ad.push(ch);
}
while (!ad.isEmpty() && k > 0) { // 没丢弃足够个数的数字 要补足
ad.pop();
k--;
}
StringBuilder sb = new StringBuilder();
while (!ad.isEmpty()) sb.insert(0, ad.pop()); // 栈后入先出 字符串要逆序组合
while (sb.length() > 0 && sb.charAt(0) == '0') sb.deleteCharAt(0); // 删除前导0
if (sb.length() == 0) sb.append('0'); // 如果最终sb被清空 则结果为0 补上
return sb.toString();
}
}
修改后的代码 速度稍微快点 思路没变 只是省了一些中间操作
import java.util.*;
import java.lang.*;
public class Main {
static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
char[] chs = scanner.next().toCharArray();
int k = scanner.nextInt();
int n = chs.length;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
char ch = chs[i];
int pos = sb.length() - 1;
while (pos >= 0 && sb.charAt(pos) > ch && k > 0) { // 往前找第一个不大于ch的数字位置
pos--;
k--;
}
if (pos < sb.length() - 1) sb.delete(pos + 1, sb.length()); // 大于ch的数字整段移除
sb.append(ch);
}
if (k > 0) sb.delete(sb.length() - k, sb.length()); // 补足k个
k = 0;
n = sb.length();
while (k < n && sb.charAt(k) == '0') k++; // 往后找第一个不为0的数字位置
if (k > 0) sb.delete(0, k); // 前导0整段移除
System.out.println(sb.length() == 0 ? "0" : sb.toString());
}
}