java—如何在保留最小词典顺序的同时删除重复字母

tkqqtvp1  于 2021-07-03  发布在  Java
关注(0)|答案(4)|浏览(197)

我有一个任务,从给定的字符串(经典的面试问题)删除重复,但这是一个有点不同,最终的结果应该在最小的词典顺序可能的其他。例如, cbacdcbc => acdb, bc => . 我在so中看到了几个相关的问题,但我找不到答案。
编辑:这是我目前的代码(工作不正常):

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();
 }

edit2:很抱歉没有把问题的链接放在前面。以下是链接:

baubqpgj

baubqpgj1#

通过从输入的末尾向后到开始构建结果。在每个步骤上:
如果遇到新的字母,请将其置于结果之前。
如果遇到重复,则将其与结果的开头进行比较。如果head更大,则从结果中删除duplicate并将其前置。 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();
}
ccrfmcuu

ccrfmcuu2#

观察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))
iqih9akk

iqih9akk3#

首先,让我们创建一组字符串中所有不同的字母 s . 这个集合的大小是答案的长度和我们算法中的步数。我们将在每个步骤的答案上添加一个字母,方法如下:
在每一个步骤中,按字母顺序遍历剩余的字母 l :
查找第一次出现的 ls . 我们来命名吧 lpos .
如果子串 s[lpos, end] 包含所有剩余字母,然后添加 l 结果,替换 ss[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')); 
}

可运行版本:https://ideone.com/wiki3x

x7rlezfr

x7rlezfr4#

这可以在o(stringlenght)时间内完成。串长=n;时间复杂度o(n),字符串的单次扫描。空间复杂性o(26)
解决方案:
创建一个字母数组,它将存储指向双链接列表节点的指针。 ListNode* array[26]; // Initialized with NULL value. 创建一个空的linkedlist,它将在任何时间点以相反的顺序表示解决方案字符串。
扫描字符串,检查每个字母的字母,ltr,check array[ltr-'a'] .... a、 )如果为空,则表示它是第一次出现,并将其添加到链表中。。。b、 )如果数组指向任何节点listnodeltr,则表示结果i中已经有字母。检查链接列表中listnode到listnodeltr的前一个listnode的值,
如果值 listNodeltr->prev->val < listNode->val ,这意味着从该位置移除当前节点将使结果变小。所以从linkedlist中的当前位置删除listnodeltr并将其添加到末尾。

Else, current postion of ltr is find and continue.

cbacdcbc公司
[a] ->[b]->[c]
cbacdcbc[c]->[a]->[b]
cbacdcbc[d]->[c]->[a]->[b]
cbacdcbc[b]->[d]->[c]->[a]
cbacdcbc[b]->[d]->[c]->[a]
按相反顺序打印链接列表:acdb。

相关问题