字典序最小字符串问题看似简单,实则暗藏玄机。给定一个由小写字母组成的字符串,我们可以进行任意次数的删除操作——每次选择至少出现两次的字符,删除其中的一个实例。我们的目标是找到通过这些操作能得到的字典序最小的字符串。
字典序比较的规则需要特别注意:从左到右逐字符比较,第一个不同字符决定了整个字符串的顺序。如果所有对应字符都相同,则较短的字符串被视为字典序更小。例如"aab" < "aac",而"abc" < "abcd"。
这个问题的难点在于:
关键洞察在于:字典序最小的字符串应该尽可能保留前面的小字符。这意味着:
原解法采用了二进制掩码来表示删除操作,这是一个系统但不够高效的方案。让我们分析其核心步骤:
虽然这种方法能保证找到最优解,但对于长字符串(如10^5长度)会产生不可行的计算量。
实际上,这个问题可以通过单调栈+贪心的方法更高效地解决。基本思路是:
这种方法的时间复杂度是O(n),空间复杂度是O(1)(因为字母表大小固定)。
让我们深入分析原始代码的关键函数:
python复制def get_more_than_two_times_char_list(a):
a = list(a)
b = set(a)
t = []
for i in b:
t.append([i, a.count(i)])
d = list(i for i in t if i[1] >= 2)
d.sort(key=lambda x: x[0])
return d
这个函数统计每个字符的出现次数,筛选出重复字符。优化建议:
python复制def get_combination_index(a, b, n):
t = []
for i in a:
for j in b:
xi = int(i, 2)
xj = int(j, 2)
k = xi | xj
x = bin(k)[2:]
len_x = len(x)
x = (n - len_x) * '0' + x
t.append(x)
return t
这个函数通过二进制或运算组合不同字符的删除选择。主要问题:
以下是改进后的高效实现:
python复制def removeDuplicateLetters(s):
from collections import defaultdict
count = defaultdict(int)
for ch in s:
count[ch] += 1
stack = []
in_stack = set()
for ch in s:
count[ch] -= 1
if ch in in_stack:
continue
while stack and ch < stack[-1] and count[stack[-1]] > 0:
in_stack.remove(stack.pop())
stack.append(ch)
in_stack.add(ch)
return ''.join(stack)
这个实现:
设字符串长度n,重复字符数量k:
显然无法处理大规模输入。
可以轻松处理10^5长度的字符串。
无重复字符:
全相同字符:
需要策略性删除:
空字符串:
单字符:
所有字符相同:
优点:
缺点:
以下是经过优化的完整实现,包含详细注释:
python复制def smallestSubsequence(s):
"""
返回删除重复字符后的字典序最小字符串
:type s: str
:rtype: str
"""
from collections import defaultdict
# 统计每个字符的剩余出现次数
remaining = defaultdict(int)
for ch in s:
remaining[ch] += 1
# 使用栈构建结果
stack = []
# 记录栈中已有字符
in_stack = set()
for ch in s:
# 每处理一个字符,剩余计数减1
remaining[ch] -= 1
# 如果字符已在栈中,跳过
if ch in in_stack:
continue
# 弹出栈顶比当前字符大且后面还会出现的字符
while stack and ch < stack[-1] and remaining[stack[-1]] > 0:
in_stack.remove(stack.pop())
# 将当前字符入栈
stack.append(ch)
in_stack.add(ch)
return ''.join(stack)
这个实现通过了LeetCode所有测试案例,时间复杂度O(n),空间复杂度O(1)。