=====
题目描述如下:
给你一个字符串 s
,一个整数 k
,一个字母 letter
以及另一个整数 repetition
。
返回 s
中长度为 k
且 字典序最小 的子序列,该子序列同时应满足字母 letter
出现 至少 repetition
次。生成的测试用例满足 letter
在 s
中出现 至少 repetition
次。
子序列 是由原字符串删除一些(或不删除)字符且不改变剩余字符顺序得到的剩余字符串。
字符串 a
字典序比字符串 b
小的定义为:在 a
和 b
出现不同字符的第一个位置上,字符串 a
的字符在字母表中的顺序早于字符串 b
的字符。
示例 1:
输入:s = "leet", k = 3, letter = "e", repetition = 1 输出:"eet" 解释:存在 4 个长度为 3 ,且满足字母 'e' 出现至少 1 次的子序列: - "lee"("leet") - "let"("leet") - "let"("leet") - "eet"("leet") 其中字典序最小的子序列是 "eet" 。
=====
算法思路:
我们这里用e代表题目中的letter以方便描述。由于题目要求的条件比较多,我们先考虑简单的情况,即,如何找到 s
中长度为 k
且字典序最小的子序列。
对于所有长度为k的子序列,第一个字符肯定要是字典序最小的。如果有多个子序列,它们的第一个字符字典序最小且相同,那么我们需要从它们当中找出第二个字符字典序最小的那些序列。以此类推,直到找出符合条件的子序列。
其实这就是要求我们从原字符串中找出: 最小字符,次小字符,第三小字符,等等,这样一个字符串,且它们的索引必须是递增的。因此我们可以使用“栈”的数据结构来找出它们。算法如下:
1. 我们设数组res做为此栈。设s_len为s的长度。
2. 然后我们从左往右枚举原字符串s。只要s[i] < res[-1],我们弹出栈顶元素res[-1]。重复此判断直到s[i] >= res[-1]。我们把s[i]放入res。
注: 由于我们需要维持总长度k,所以在步骤2中有时不能把res里的元素全部弹出,需要保证以下不等式成立:
(res长度+ [i, n-1]长度) >= k
对于不能弹出的情况,直接把s[i]放入res。
枚举完成后,res中存储的就是: 最小字符,次小字符,第三小字符,等等,这样一个字符串,且它们的索引是递增的。如果res长度大于k,我们取res中的前k个元素即为最终答案。
好了,到此时,我们就解决了题目中的一个条件。下面,我们再来看如何满足其他条件。题目还要求字母 letter
出现 至少 repetition
次。
我们发现答案中的第一个字符不可能取在倒数第repetition个e之后,因为那样的话,最后的结果e数肯定少于repetition。因此我们需要先找到倒数第repetition个e的索引,把它做为一个判断的基准,我们设变量eKeyInd来储存这个值。那么在eKeyInd之前的字符串也尽量要找出,最小字符,次小字符,第三小字符,等等。和上面的算法原理一样。
为了方便后面描述,我们再设两个变量:
eResCnt: 表示已经在res中的e的数量。
eLeftCnt: 表示eKeyInd之后还剩下e的数量,包括eKeyInd处的e。初始值为repetition。
在枚举到eKeyInd时,res会有两种情况:
1. res长度+eLeftCnt >= k,这时我们取res的前(k-eLeftCnt)个字符,然后加上eLeftCnt个e即为最终的答案。
2. res长度+eLeftCnt < k,由于不足以形成k个字符,因此我们需要从eKeyInd到下一个e出现的索引中间再尝试找出一段递增的字符串。这样形成一个新的res。由于eKeyInd处的e已经添加进去res,因此eLeftCnt需要减1。然后重复判断这两种情况,直至找出答案。
在弹出栈顶的过程中,如果碰到栈顶res[-1]字符为e时,我们还需要确保eResCnt+eLeftCnt>repetition。这样才能弹出e,且eResCnt要减1。否则不能弹出e。
同时,在入栈时,如果当前字符为e,也要相应把eResCnt加1。
对于额外的条件满足,算法总体的思路就是上面提到的两种情况。其他的地方就是要注意一些细节的判断,以保证最后算法的准确。
算法的时间复杂度为O(n)。下面是Python代码实现:
class Solution(object):
def smallestSubsequence(self, s, k, letter, repetition):
"""
:type s: str
:type k: int
:type letter: str
:type repetition: int
:rtype: str
"""
s_len=len(s)
res=[]
eKeyInd=0
eLeftCnt=0
eResCnt=0
for i in range(s_len-1, -1, -1):
if s[i]==letter:
eLeftCnt+=1
if eLeftCnt==repetition:
eKeyInd=i
break
for i in range(s_len):
while res and s[i]<res[-1] and len(res)+s_len-i > k:
if res[-1]==letter:
if eResCnt+eLeftCnt==repetition:
break
else:
eResCnt-=1
res.pop()
if s[i]==letter and i>=eKeyInd:
if len(res)+eLeftCnt>=k:
break
else:
eLeftCnt-=1
res.append(s[i])
if s[i]==letter:
eResCnt+=1
res=res[:k-eLeftCnt]+[letter]*eLeftCnt
res="".join(res)
return res
关键词: 单调栈