这道题让我们移除重复字母,使得每个字符只能出现一次,而且结果要按字母顺序排,前提是不能打乱其原本的相对位置。
这道题我一开始用的dfs,也就是碰到重复的字母,要么把这个加进去,把前面重复的那个删掉,要么不把这个加进去,保留前面那个。但是超时……代码如下:
class Solution {
public:
string removeDuplicateLetters(string s) {
set<string> res;
helper(s, 1, s.substr(0, 1), res);
return *res.begin();
}
void helper(string s, int start, string out, set<string>& res){
if(start >= s.size())
res.insert(out);
for(int right = start; right < s.size(); ++right){
if(find(out.begin(), out.end(), s[right]) == out.end()){
out.push_back(s[right]);
//helper(s, right+1, out, res);
}else{
helper(s, right+1, out, res);//不要right这个位置的字符
out.erase(find(out.begin(), out.end(), s[right]));
out.push_back(s[right]);
helper(s, right+1, out, res);//要right这个位置的字符,把前面的删掉
}
}
res.insert(out);
}
};
看了大神的代码,发现自己确实想不到,这道题应该这样做:
建立一个数组或者哈希表,遍历字符串,记录每个字母出现的次数。再定义一个数组visit表示每个字符是否访问过
遍历字符串,遇到一个字符s[i],如果这个字符访问过,跳过。如果这个字符没有访问过,那么比较res里面最后加进去的这个字符res.back(),如果s[i]<res.back(),而且res.back()这个字符在m里还有多余的,说明res.back()可以先删掉,在后面加,直到不满足s[i]<res.back()或者m[res.back()]>0,就把s[i]放在这个位置。
让我想了半天的是这个visit数组,为什么visit[i]为1的时候要跳过呢,因为visit[i] = 1的时候,说明截止到目前为止,s[i]这个字母已经放好了。所以visit[i]不能说是访问过,而是表示是否已经放好了。如果一个字符本来放好了又被换下来了,那么他的visit会是0;
这种与重复字符有关的题,可以想哈希表的方法记录原字符串中每个字符出现的次数,然后再按照每个题目不同的要求往下做,这种思路已经遇到过很多次。
class Solution {
public:
string removeDuplicateLetters(string s) {
vector<int> m(256, 0);
vector<int> visit(256, 0);
string res = "0";
for(auto a : s){
m[a]++;
}
for(auto a : s){
--m[a];
if(visit[a]) continue;
while(a < res.back() && m[res.back()]){
visit[res.back()] = 0;
res.pop_back();
}
res.push_back(a);
visit[a] = 1;
}
return res.substr(1);
}
};