Given a string which contains only lowercase letters, remove duplicate letters so that every letter appear once and only once. You must make sure your result is the smallest in lexicographical order among all possible results.
Example 1:
Input: “bcabc”
Output: “abc”
Example 2:
Input: “cbacdcbc”
Output: “acdb”
给出一个字符串,让output出unique的字符,
而且字符的顺序:比如上例的“bcabc",当前面和后面都有bc的时候,因为a在字符顺序上位于bc前面,所以只输出后面的bc,unique输出是abc
第二个例子,c的字母顺序在d前面,尽管后面有c,也是输出cd,全体是acdb
思路:
先读一遍字符串,记下每个字符出现了几次,记在count数组中。
字母只有26个,直接用数组即可,不需要HashMap。
然后再读一遍,这次要把字母保存到result字符串中。同时用过的字母它对应的次数要-1。
我们要保证result中字母是排好序的,怎么做到呢?就是每次来一个新字母的时候都要把前面比它大的删掉,当然如果前面比它大的仅此一个(用到了出现次数),后面没了,就不能删了。
如果result为空,直接把字母append进去,同时用一个visited数组标记这个字母已经在result里面了,如果下次s中出现了同样的,就不需要再处理了,直接跳过。
因为result中是已经按要求排好序的,所以不必纠结每次都要遍历一遍result,都要去和当前s的字母比较,只需要把result最后的字母(最大的)和s当前字母比,如果比s[i]小,就直接append s[i],不需再比。
如果result最后的字母比s[i]大,是不是就要删除?不一定,因为如果后面没有这个字母了怎么办,不就少一个字母了。
这时count数组起作用了,它记录了每个字母出现的次数,如果次数为0了,说明后面没有了,不能删。
如果后面还有,那么删掉,再取 下一个result的最后字母,直到不满足条件或result为空为止。
public String removeDuplicateLetters(String s) {
int[] cnt = new int[26];
int n = s.length();
boolean[] visited = new boolean[26];
StringBuilder result = new StringBuilder();
for(int i = 0; i < n; i ++) {
cnt[s.charAt(i) - 'a'] ++;
}
for(int i = 0; i < n; i ++) {
int len = result.length();
char sc = s.charAt(i);
cnt[sc - 'a'] --; //用过字母的次数-1
if(visited[sc - 'a']) continue; //已经在result里面的不需要再处理
while(len > 0 && result.charAt(len-1) > sc &&
cnt[result.charAt(len-1) - 'a'] > 0) {
visited[result.charAt(len-1) - 'a'] = false; //删掉了,result中没有了,要在删掉之前,因为result.length会变
result.deleteCharAt(len - 1);
len --;
}
result.append(sc);
visited[sc - 'a'] = true;
}
return result.toString();
}