題目連結:402. Remove K Digits
解題思路#
這一題一開始我想到 O(n^2) 的方法:
- 迭代 num 陣列,每次新增一個數字
n
進 list 中。 - 當 list 的長度大於
num.length-k
後,將 list 中的元素從頭到尾與剛剛新增的數字n
進行比較,當找到第一個大於n
的數字後將它 pop 掉,如果都沒有就把剛剛新增的n
pop 掉。 - 最後去除開頭為
'0'
的部分便是答案了。
但想當然的一定會 TLE(num.length 最大到 10^5)。
如果要在 O(n) 時間來解決這個問題,那便會需要用到單調隊列(monotonic stack)的概念。解決這題的核心在於:我們希望左邊的數字越小越好,因此可以透過維護一個遞增的單調隊列來完成,只要:
- k > 0(也就是還可以移除字元)。
num[i-1] > num[i]
,代表把num[i-1]
刪除後以num[i]
取代可以得到較小的數字。
那便可以把隊列頂部(最大)的數字 pop 出來以 num[i]
來取代。
但還要考慮一個情況:
如果整個 num
都跑過一遍了,這時如果 k > 0 是甚麼意思呢?這代表說不需要移除 k 個數字就可以做出一個遞增的單調隊列。但題目要求一定要移除 k 個數字呀!怎麼辦呢?
如果移除最左邊的數字,那這樣得出來的結果就會變大(因為數字是遞增的,例如:“123” 想取兩位數,移除 “1” 的話會得到 “23”,但最小的結果是 “12”),所以從尾端開始移除數字直到隊列的長度等於 num.length-k
即可!
程式碼#
C++#
class Solution {
public:
string removeKdigits(string num, int k) {
string res;
std::stack<int> stack;
for (char c: num) {
while (!stack.empty() && k > 0 && stack.top() > c) {
stack.pop();
k--;
}
stack.push(c);
}
while (k > 0) {
stack.pop();
k--;
}
while (!stack.empty()) {
res += stack.top();
stack.pop();
}
reverse(res.begin(), res.end());
// remove leading 0
size_t pos = res.find_first_not_of('0');
if (pos != string::npos)
res = res.substr(pos);
else
res = "0";
return res;
}
};
Python#
class Solution:
def removeKdigits(self, num: str, k: int) -> str:
stack = []
for c in num:
while stack and c < stack[-1] and k > 0:
stack.pop()
k -= 1
stack.append(c)
if k > 0:
stack = stack[:-k]
res = "".join(stack).lstrip("0")
return res if res != "" else "0"