我有一项任务是从给定的字符串中删除重复项(经典访谈问题),但这个有点不同,最终结果应该是最小的字典顺序。例如,public static String removeDuplicateCharsAlphbetically(String str) {
int len = str.length();
if (len<2) return str;
char[] letters = str.toCharArray();
int[] counts = new int[26];
for (char c : letters) {
counts[c-97]++;
}
StringBuilder sb = new StringBuilder();
for (int i=0;i<len-1;i++) {
if (letters[i]==letters[i+1]) continue;
if (counts[letters[i]-97]==1) {
sb.append(letters[i]);
} else if (counts[letters[i]-97] != 0) {
if (letters[i]<letters[i+1] && counts[letters[i]-97] == 1) {
sb.append(letters[i]);
counts[letters[i]-97]=0;
} else {
counts[letters[i]-97]--;
}
}
}
return sb.toString();
}
。我在SO中看到了几个相关问题,但我找不到答案。
修改:到目前为止,这是我的代码(工作不正常):
{{1}}
EDIT2 :我很抱歉没有提前提问。这是link:
答案 0 :(得分:3)
这可以在O(StringLenght)时间完成。 字符串长度= N; 时间复杂度O(N),字符串的单次扫描。 空间复杂度O(26)
解决方案:
创建一个字母数组,该数组将存储指向双链表Node的指针。 ListNode *数组[26]; //用NULL值初始化。
创建一个空的链表,该链表将在任何时间点以相反的顺序表示解决方案字符串。
扫描字符串,然后为每个字母检查字母,ltr,检查数组[ltr-'a'] 一种。如果为NULL,则表示它是第一次出现,并将其添加到链接列表中。 b。如果数组指向任何节点listNodeltr,则表示字母已经在结果中 一世。在linklist
中检查上一个listNode到listNodeltr的值如果listNodeltr-> prev-> val
否则,找到并继续使用ltr的当前位置。
cba cdcbc
[a]-> [b]-> [c]
cbac dcbc [c]-> [a]-> [b]
cbacdc bc [d]-> [c]-> [a]-> [b]
cbacdcb c [b]-> [d]-> [c]-> [a]
cbacdcbc [b]-> [d]-> [c]-> [a]
以相反的顺序打印链接列表:acdb。
答案 1 :(得分:2)
首先,让我们创建一组字符串s
的所有不同字母。该集合的大小是答案的长度和算法中的步骤数。
我们将使用以下贪婪的方法在每个步骤中为答案添加一个字母:
在每一步中按字母顺序和每个字母l
迭代剩余的字母:
l
中查找s
的第一个匹配项。我们将其命名为lpos
。s[lpos, end]
包含所有剩余字母,则将l
添加到结果中,将s
替换为s[lpos+1, end]
,然后使用缩小的剩余字母集进行下一步。 通过一些优化实现以实现更好的时间复杂性:
public String removeDuplicateLetters(String s) {
StringBuilder result = new StringBuilder();
int[] subsets = new int[s.length()];
int subset = 0;
for (int i = s.length() - 1; i >= 0; i--) {
char ch = s.charAt(i);
subset = addToSet(subset, ch);
subsets[i] = subset;
}
int curPos = 0;
while (subset != 0) {
for (char ch = 'a'; ch <= 'z'; ++ch) {
if (inSet(subset, ch)) {
int chPos = s.indexOf(ch, curPos);
if (includes(subsets[chPos], subset)) {
result.append(ch);
subset = removeFromSet(subset, ch);
curPos = chPos + 1;
break;
}
}
}
}
return result.toString();
}
private boolean inSet(int set, char ch) {
return (set & (1 << (ch - 'a'))) != 0;
}
private boolean includes(int set, int subset) {
return (set | subset) == set;
}
private int addToSet(int set, char ch) {
return set | (1 << (ch - 'a'));
}
private int removeFromSet(int set, char ch) {
return set & ~(1 << (ch - 'a'));
}
Runnable版本:https://ideone.com/wIKi3x
答案 2 :(得分:2)
通过从输入结尾向后开始构建结果。在每一步:
LinkedHashSet
对于存储结果集及其内部顺序都很好。
public static String unduplicate(String input) {
Character head = null;
Set<Character> set = new LinkedHashSet<>();
for (int i = input.length() - 1; i >= 0; --i) {
Character c = input.charAt(i);
if (set.add(c))
head = c;
else if (c.compareTo(head) < 0) {
set.remove(c);
set.add(head = c);
}
}
StringBuilder result = new StringBuilder(set.size());
for (Character c: set)
result.append(c);
return result.reverse().toString();
}
答案 3 :(得分:1)
观察1:输出的第一个字母是最小字母,使得所有其他字母出现在字符串中最左边的外观的右侧。
观察2:输出的剩余字母是第一个字母最左边外观右边字母的子序列。
这表明了一种递归算法。
def rem_dups_lex_least(s):
if not s:
return ''
n = len(set(s)) # number of distinct letters in s
seen = set() # number of distinct letters seen while scanning right to left
for j in range(len(s) - 1, -1, -1): # len(s)-1 down to 0
seen.add(s[j])
if len(seen) == n: # all letters seen
first = min(s[:j+1])
i = s.index(first) # leftmost appearance
return first + rem_dups_lex_least(''.join(c for c in s[i+1:] if c != first))