LeetCode316.Remove Duplicate Letters--贪心算法

来源:互联网 发布:端口参数液晶智能电视 编辑:程序博客网 时间:2024/06/05 21:14

316.Remove Duplicate Letters

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:
Given “bcabc”
Return “abc”

Given “cbacdcbc”
Return “acdb”

题目大意是,给定一个字符串ss只由小写字母组成。要求去掉s中重复的字母,且当有多个结果时,要返回字母序最小的结果。

根据题目不难想到可以用贪心算法来求解:
假设得到的目标结果为s,那么最理想情况下自然是s[0] == 'a's[1] == 'b'…(假设字母a和b都存在的话。由此,我们可以先令result为空,每次在后面加上一个字母。且满足:这个字母为当前字符串中字母序最小的一个。但是,为了确保不会将原字符串s中含有的字母去掉,在这个基本条件以外,还需要满足一个特殊条件,即若某一字符只在字符串中出现过一次,那么它必须立刻被选择
还要注意的是,当一个字母ch符合条件并被选择以后,可以做到两点:

  • ch之前的所有字母,都可以去除,因为它们的字母序都大于ch,或者它们在ch之后还要候补的字母,可以在之后再加入到result中。
  • ch之后的所有ch都可以去除,因为ch以及尽可能早地加入了result

当确认了这些条件之后,不难得到整个代码实现:

string Solution::removeDuplicateLetters(string s) {    if (s.length() == 0) {        return "";    }    int count[26] = { 0 };    int pos = 0;    for (char x : s) count[x - 'a']++;    for (int i = 0; i < s.length(); i++) {        if (s[i] < s[pos]) pos = i;        if (--count[s[i] - 'a'] == 0) break;    }    string replaced = s.substr(pos + 1);    for (auto it = replaced.begin(); it != replaced.end(); ) {        if (*it == s[pos]) {            it = replaced.erase(it);        }        else {            it++;        }    }    return s[pos] + removeDuplicateLetters(replaced);}

在通过题目后,我在本题的Discuss中,看到了一种更优的解法。这种解法,也是一种贪心算法,只不过运用得更加巧妙。算法思路如下:
在这种贪心算法中,同样是遍历整个字符串s,但是它的基本思路是,每次都先将当前字母ch加入到result中。但是,当发现当前字符ch的字母序,小于result.back(),则将result的最后一位移除,并以ch来替换。这个替换过程也是迭代的,会一直重复到不能再替换为止。这里也存在着另一个条件,即被替换的字母在后面的字符串中必须还有候补,否则不能替换。以此保持不去除不重复的字母。
根据这个思路,便可以得到另一种解。在代码实现中,我使用了count数组来存储每个字母的剩余数量,而用exist来代表某个字母是否已经在result中出现过。代码实现如下:

string Solution::removeDuplicateLetters(string s) {    if (s.length() == 0) {        return "";    }    int count[26] = { 0 };    int pos = 0;    for (char x : s) count[x - 'a']++;    for (int i = 0; i < s.length(); i++) {        if (s[i] < s[pos]) pos = i;        if (--count[s[i] - 'a'] == 0) break;    }    string replaced = s.substr(pos + 1);    for (auto it = replaced.begin(); it != replaced.end(); ) {        if (*it == s[pos]) {            it = replaced.erase(it);        }        else {            it++;        }    }    return s[pos] + removeDuplicateLetters(replaced);}

两种算法相比较,第一种在每一层递归中都是O(n),而第二种复杂度则单纯为O(n),显然效率更高。

原创粉丝点击