编程题:字符串去重,找到最小字典序结果(leetcode 316)

题目:给定一个字符串s,删去字符串中的重复字母,保证结果中原有字符的先后顺序不变,找到最小字典序的结果
示例:给定s:'bcabc',去重后的结果可以是'abc', 'bac', 'bca', 'cab',其中字典序最小的是'abc'

解法1:暴力回溯法,找到所有重复的字符,按重复的字符回溯,每次选择一个位置保留,其他位置删除,直到所有重复字符都处理完,此时为一个合法的去重字符,回溯找到所有的字符,排序得到字典序最小的结果,时间复杂度很高

char_cnt = {}
rst = []

def find_least_substr(s):
    global char_cnt, rst
    rst = []
    char_indice = {}
    i = 0
    for ch in s:
        char_cnt[ch] = char_cnt.get(ch, 0) + 1
        char_indice[ch] = char_indice.get(ch, []) + [i]
        i += 1
    char_indice = [[ch, indice]
                   for ch, indice in char_indice.items() if len(indice) > 1]
    back_trace(list(s), char_indice, 0)


def back_trace(s, char_indice, n):
    global rst
    if n == len(char_indice):
        rst.append(''.join(s).replace('_', ''))
        return
    ch, indice = char_indice[n]

    for idx in indice:
        for j in indice:
            if j != idx:
                s[j] = '_'

        back_trace(s, char_indice, n+1)
        for j in indice:
            if j != idx:
                s[j] = ch


find_least_substr('bcabc')
rst.sort()
print(rst)

解法2:贪心算法,从左往右找,直到出现第一个所有重复已经出现的字符(意味着不能继续再寻找,必须做出选择了),此时贪心地选择当前位置及之前字典序最小的字符作为留下的字符,迭代进行以上操作,如果字符串中都是小写字母的话,时间复杂度最差为26*n,也就是O(n)

def char_idx(ch):
    return ord(ch) - ord('a')

def find_least_substr(s):
    if not s:
        return ''
    cnt = [0]*26
    pos = 0 # 记录字典序最小的字符,作为贪心选择的位置
    for ch in s:
        cnt[char_idx(ch)] += 1
    for i, ch in enumerate(s):
        if s[i] < s[pos]:
            pos = i
        cnt[char_idx(ch)] -= 1
        # 所有重复都已经找到,必须做出选择
        # 第一个字符一定要在已遍历过的字符中选择,此时贪心选择一定是最优的
        if cnt[char_idx(ch)] == 0:
            break
    return s[pos] + find_least_substr(s[pos+1:].replace(s[pos], ''))


rst = find_least_substr('addcccsfdsgbcabhgfcdef')
print(rst)
posted @ 2021-10-29 14:16  排骨zzz  阅读(522)  评论(0编辑  收藏  举报