编程题:字符串去重,找到最小字典序结果(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)