Hello
大家好🌼🌼,我是 @愿此后再无WA,可以叫我小A,也可以叫我愿愿💡💡,一位阳光帅小伙,对算法领域比较感兴趣。如果我的文章对您有用,欢迎持续关注,我们一起进步!🎈🎈
游园安排
吐槽
做这题收获挺多的,如果各位看到我的这句话的话一定得把这道题啃下去。
分析
这道题的原型是最长递增子序列(LIS),如果不明白最长递增子序列的朋友可以点击这里。up主声音很小,但讲的思路还是可以的,记住要学会nlogn的优化算法哈,而不是n^2的算法噢。
当然你不想看也没关系,如果你理解下面这段话的话…
这是力扣官方的思路,原文链接
如果你想更好的掌握这个模板的话建议来这练练手,上面有原题。
当你看到这里的时候说明你已经掌握了LIS的基本思路。在他nlogn的优化算法中,是采用二分搜索等于或第一个大于它的元素,但如果使用我们自个写的二分的话会超时,毕竟python比较慢,决赛题数据确实是挺大的。
s = input()
words = []
i = 0
length = len(s)
while i < length:
if s[i].isupper():
word = s[i]
i += 1
while i < length and s[i].islower():
word += s[i]
i += 1
words.append(word)
state = [1]
strs = [words[0]]
for i in words[1:]:
'''
if i == strs[-1]:
print(i)
continue
'''
if i > strs[-1]:
strs.append(i)
state.append(len(strs))
else:
l = 0
r = len(strs) - 1
k = r
while l <= r:
mid = l + (r-l) // 2
if strs[mid] >= i:
k = mid
r = mid - 1
else:
l = mid + 1
strs[k] = i
state.append(k+1)
m = max(state) # 获取最大长度
output = []
#print(len(state),len(words))
for i in range(len(words)-1,-1,-1):
if state[i] == m:
output.append(words[i])
m -= 1
while output:
print(output.pop(),end="")
虽然没有提示超时但确确实实已经超过一秒了,放在比赛不给我们过的话就血亏。而python有个标准库bise
ct能帮助我们优化。
bisect本质上应该还是二分搜索,但如果使用它的话真真切切能够提高速度。
它有两个功能,一个是查找,一个是插入,都是在有序列表中进行的,而且是升序。关于它的具体用法可以跟着小郑一起学习一下,点击这里
ok,学完LIS跟bisect之后就来看下一个问题。
这道题比LIS更棘手的地方就是还要将字母打印出来,我们知道LIS优化的算法里面维护的那个数组(记为words)里面的元素与结果没有太大的联系,唯一能够联系的地方就是这个维护数组的长度与最大递增子序列长度对应。而如果要得到它所经过的路径是做不到的。那么这里就需要进行路径还原,进行路径还原的话我们要多开一个与总单词个数一致的数组(记为state),分别记录以每个单词结尾所能达到的最大长度。比如…我们以数字为例,数字大小代表单词的ASCII的大小。假设起初words数组是这样的:1 4 7 9,此时state 存的数据是 1 2 3 4(第一个数字1能到达的最大长度是1,第二个数字能到达的最大长度是2,第三个…),因为前面四个数是不断递增的,还没有遇到比最后一个元素小的元素所以长度也是递增的。紧接着第5个数5出现了,5比9小,那么就要替换里面的元素,替换谁呢,替换掉7。当替换掉7后,数组变成1 4 5 9,那么在这里以5结尾的最大长度就是3(1 4 5),因此将3加入到state里面。就是如果比最后一个数大就直接增加长度,否则替换掉里面的值,然后长度也截断存入到state数组中,遍历到最后的话就能得出每个元素对应达到的最大长度了。
最后我们得到的这个state数组并不是有序的,我们从里面最大的值开始从大到低的选,直到选完每个长度的一个数为止。比如 state数组是 122345364,最大值是6也就是这个递增子序列的最大长度是6,然后从后往前选依次选完654321,就是每个长度对应的那个元素。当出现多个相同的数时,我们选后面那个。以刚才的数组122345364为例,前面有两个2,选2的话要选后面那个,因为它更有潜力,之所以在2后面还能出现2是因为2对应的那个元素的值必定小于等于前面的2的值,才会重新出现2,那么我现在我们看一下代码实现。
代码
import bisect
S = input()
words = []
s = ""
for i in S: # 获取S里面的里面的每个单词
if i.isupper():
if s:
words.append(s)
s = i
else:
s += i
words.append(s) # 将最后一个单词放入words中
wds = [words[0]] # 从第一个单词开始
state = [1]
for i in words[1:]:
if i > wds[-1]: # 依次与wds的最后一个元素对比
wds.append(i) # 如果大于则直接加入到尾部
state.append(len(wds)) # 长度加一
else: # 小于等于都要替换掉里面的元素
k = bisect.bisect(wds,i) # 使用bisect查找
if k > 0 and wds[k-1] == i: # 如果右索引的前一个数等于这个i的话,就将它替换掉
k -= 1 # 否则就替换到第一个大于i的元素
wds[k] = i
state.append(k+1) # 将这个k对应的长度加入到state中
m = max(state) # 获取递增子序列的最大长度
output = [] # 因为结果是正序的,而我们是从后往前找的,找到的单词是逆序的,所以要先存储再倒序输出
for i in range(len(words)-1,-1,-1):
if state[i] == m: # 逐步获取最靠右的每个长度对应的元素
output.append(words[i])
m -= 1
for i in output[::-1]: # 倒序输出
print(i,end="")