My code:
public class Solution {
public String removeDuplicateLetters(String s) {
if (s == null || s.length() == 0)
return s;
int len = s.length();
Stack<Character> tracker = new Stack<Character>();
HashMap<Character, Integer> dic = new HashMap<Character, Integer>(); // character -> numbers of this character
/** form this dictionary to record the numbers of characters */
for (int i = 0; i < len; i++) {
char curr = s.charAt(i);
if (!dic.containsKey(curr))
dic.put(curr, 1);
else {
int times = dic.get(curr);
dic.put(curr, times + 1);
}
}
/** remove elements from string */
for (int i = 0; i < len; i++) {
char curr = s.charAt(i);
if (tracker.isEmpty())
tracker.push(curr);
else if (tracker.contains(curr)) {
int times = dic.get(curr);
dic.put(curr, times - 1);
continue;
}
else {
while (!tracker.isEmpty()) {
int times = dic.get(tracker.peek());
if (curr < tracker.peek() && times > 1) {
dic.put(tracker.peek(), times - 1);
tracker.pop();
}
else {
tracker.push(curr);
break;
}
}
if (tracker.isEmpty())
tracker.push(curr);
}
}
StringBuilder ret = new StringBuilder();
while (!tracker.isEmpty()) {
ret.append(tracker.pop());
}
return ret.reverse().toString(); // string does not have reverse() method
}
}
這道題木我沒有做出來。看了答案才想出來。
一開始我寫了我的解法,就是看右邊的值是否比自己小。小的化就刪除自己。后來發(fā)現(xiàn),右邊第一個(gè)可能比自己大,但是右邊第二個(gè)比自己小的時(shí)候,依然得把自己刪除。然后我就不知道怎么繼續(xù)寫了。看了答案。拿Stack來做。很巧妙。
首先,為什么拿Stack來做呢?他的什么特性復(fù)合這道題木呢?
我們需要拿后面的字母和前面的作比較,進(jìn)行操作,而不需要拿前面的字母和后面的字母作比較。所以,棧很合適。
做法是,先用HashMap做一個(gè)字典。統(tǒng)計(jì)每個(gè)字母出現(xiàn)的個(gè)數(shù)。
然后遍歷整個(gè)字符串。
當(dāng)棧為空的時(shí)候,直接插入。
非空時(shí),
當(dāng)插入的元素,棧中已經(jīng)存在了,那么就舍棄,直接continue;
當(dāng)插入的元素,比棧頂元素值要小時(shí),判斷該棧頂元素的對(duì)應(yīng)counter是否大于1,如果大于1,說明以后還會(huì)出現(xiàn)。那么,就把該元素pop出來。然后拿插入元素和新的棧頂元素比較,進(jìn)行相同操作。直到最后棧空或者棧頂元素小于該插入元素時(shí),就向棧插入該元素。
這一步很關(guān)鍵,他保證了,棧中的元素,從底部到頂部,值是從小到大的。除非某些元素一共只出現(xiàn)了一次,那沒辦法。
然后新的插入元素出現(xiàn)時(shí),如果棧頂元素,即他左側(cè)的字母,是可以刪除的(counter > 1), 那么我們就該選擇刪除這個(gè)元素而不是后面的,因?yàn)檫@樣的話,值小的元素就能移動(dòng)到前端,滿足字符順序。
如果插入元素已經(jīng)存在在了棧中,那么丟棄該元素,也就是刪除該字母而不是前面的相同字母。為什么。因?yàn)闂J菑拇蟮叫∨帕械模丝痰淖址帜甘菑男〉酱蟮摹h除了棧中的元素,會(huì)使得字典順序變大。
還有一種解法具體我就不研究了。
參考網(wǎng)頁:
https://www.hrwhisper.me/leetcode-remove-duplicate-letters/
Anyway, Good luck, Richardo!
My code:
import java.util.HashMap;
public class Solution {
public String removeDuplicateLetters(String s) {
if (s == null || s.length() == 0) {
return s;
}
HashMap<Character, Integer> helper = new HashMap<Character, Integer>();
for (int i = 0; i < s.length(); i++) {
char curr = s.charAt(i);
helper.put(curr, i);
}
HashMap<Character, Integer> map = new HashMap<Character, Integer>();
StringBuilder ret = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char curr = s.charAt(i);
if (!map.containsKey(curr)) {
ret.append(curr);
map.put(curr, ret.length() - 1);
}
else {
int pre = map.get(curr);
int start = -1;
int end = pre + 1;
while (end < ret.length()) {
if (ret.charAt(end) > ret.charAt(pre)) {
if (find(s, i + 1, ret.charAt(end), helper)) {
end++;
}
else {
break;
}
}
else {
if (start == -1 || ret.charAt(start) > ret.charAt(end)) {
start = end;
}
if (find(s, i + 1, ret.charAt(end), helper)) {
end++;
}
else {
break;
}
}
}
if (start != -1) {
StringBuilder temp = new StringBuilder(ret.substring(0, pre));
map.clear();
for (int j = 0; j < pre; j++) {
map.put(ret.charAt(j), j);
}
for (int j = start; j < ret.length(); j++) {
temp.append(ret.charAt(j));
map.put(ret.charAt(j), temp.length() - 1);
}
temp.append(curr);
map.put(curr, temp.length() - 1);
ret = temp;
}
}
}
return ret.toString();
}
private boolean find(String s, int start, char target, HashMap<Character, Integer> helper) {
return helper.get(target) >= start;
}
public static void main(String[] args) {
Solution test = new Solution();
String ret = test.removeDuplicateLetters("mitnlruhznjfyzmtmfnstsxwktxlboxutbic");
System.out.println(ret);
}
}
沒想到之前做過這道題目,可我完全沒印象了。。。
然后自己寫了出來,提交了好多次。。。許多細(xì)節(jié)問題沒能考慮到。
先說下我的思想。其實(shí)也是棧的思想,思路起源于這道題目:
http://www.lxweimin.com/p/d6115154c548
他其中的一個(gè)子問題,給定 arr[], k, 取出k個(gè)數(shù)字組成最大的數(shù)字,保留原數(shù)組順序。
然后這道題目很像,就是當(dāng)碰到某些條件需要?jiǎng)h除時(shí),什么情況下,可以刪除。什么情況下,不能刪除。
首先,條件是什么?
條件是,當(dāng)當(dāng)前這個(gè)字符,在之前已經(jīng)出現(xiàn)過了。
ok,然后我們就需要考慮,刪不刪?
我們?nèi)〕鲈址膇ndex,從他后面開始遍歷。
這里有個(gè)細(xì)節(jié),遍歷的是之前掃出來的獨(dú)一無二的string, ret
int pre = map.get(curr);
so end = pre + 1;
while (end < ret.length()) {
....
}
然后對(duì)于遍歷的每個(gè)字符
if (ret.charAt(curr) > ret.charAt(pre)) {
...
}
else {
...
}
如果大的話,這個(gè)字符肯定不能刪,但不代表后面不會(huì)出現(xiàn)更小的。
那么我們就需要判斷,這個(gè)較大的字符,如果刪了,后面有沒有補(bǔ)充。
即,在 s[i + 1, s.length() - 1] 的范圍內(nèi),該較大的字符是否仍然存在。
如果是,那么,那么就往后繼續(xù)走,看看有沒有小的。
如果不是,直接跳出循環(huán)。
如果小的話,我們需要考慮 start 的賦值。
如果start == -1, 那么直接賦值。
如果start != -1, 那么,如果當(dāng)前的字符,同樣小于原start指向的字符,那么就更新start,否則,就略過。
然后接著判斷,是否可以繼續(xù)掃下去。原理差不多。
然后就差不多了。
然后看了下以前的解法,用Stack的。真的很簡(jiǎn)潔!
自己重寫了下:
My code:
public class Solution {
public String removeDuplicateLetters(String s) {
if (s == null || s.length() == 0) {
return s;
}
HashMap<Character, Integer> dic = new HashMap<Character, Integer>();
for (int i = 0; i < s.length(); i++) {
char curr = s.charAt(i);
if (!dic.containsKey(curr)) {
dic.put(curr, 1);
}
else {
dic.put(curr, dic.get(curr) + 1);
}
}
Stack<Character> st = new Stack<Character>();
for (int i = 0; i < s.length(); i++) {
char curr = s.charAt(i);
dic.put(curr, dic.get(curr) - 1);
if (st.isEmpty()) {
st.push(curr);
}
else if (st.contains(curr)) {
continue;
}
else {
if (curr > st.peek()) {
st.push(curr);
}
else {
while (!st.isEmpty() && st.peek() > curr && dic.get(st.peek()) > 0) {
st.pop();
}
st.push(curr);
}
}
}
StringBuilder ret = new StringBuilder();
while (!st.isEmpty()) {
ret = ret.append(st.pop());
}
return ret.reverse().toString();
}
}
Anyway, Good luck, Richardo! -- 08/25/20