虽然我是程序员,但这件事真的让我心情有点沉甸甸的。
最近,某大行发生了一件事,真的是让人看了心里发凉。
根据网上的报警截图,有个银行员工因为公司改制降薪情绪激动,居然做出了极端的行为,差点在办公区选择轻生。
报警后的警察赶到现场时,才将这位员工救了下来。
简单说,就是公司降薪惹的祸。
毕竟,不是每个人都能承受这种巨大压力,有些人可能一时想不开,就做出无法挽回的事。
但这种做法真的太极端了,生命才是最可贵的的,遇到问题真的要沉着冷静,千万不要过于极端,我们的身后还有家人。
说到这里想到了我们IT行业其实也是如此,降薪裁员还有35岁危机,很难,但是我们也要想办法应对呀,最好的办法就是不断的提升自己的技能,比如每天学一个算法题,说不定哪天就面试通过进大厂了呢
算法题:串联所有单词的子串
我们来聊聊一道算法题。其实呢,说实话,写这篇文章之前我都忍不住想了一下,"串联所有单词的子串"听起来是不是有点抽象?
这道题的核心其实挺简单的,题目大概是要求你给定一个字符串 s
和一个单词列表 words
,要求你找出 s
中所有能够由 words
中单词串联而成的子串。
简单说,就是你要从字符串 s
中找出所有的连续子串,且这些子串可以由 words
中的单词拼接成。这里的一个关键点是,单词可以不按顺序出现,但是它们必须是连续的。
这题的难点在哪?
如果单纯的考查字符串匹配或者滑动窗口那还算简单,但这题有个要点:你需要处理的是多个单词的排列组合,而且可能单词的顺序是不固定的。
所以如果我们直接暴力求解,时间复杂度就会很高。比如说,你要遍历所有的子串,并且再判断每个子串是不是 words
中单词的组合,肯定不行。
那怎么解决?
既然要找的是一个子串,且由固定单词组成,不妨从滑动窗口的角度来考虑。我们可以设定一个窗口,窗口的大小是 words
中所有单词长度的总和,然后从字符串 s
中移动这个窗口,检查这个窗口中的字符是否可以被 words
中的单词排列组合。为了加速匹配,我们可以使用哈希表来存储单词的出现次数,这样可以避免每次匹配时重复计算。
接下来,给大家写一段代码来看看怎么实现:
from collections import Counter
def findSubstring(s: str, words: list) -> list:
# 如果单词列表为空,或者字符串s为空,直接返回空列表
if not words or not s:
return []
# 单个单词的长度,words中单词的个数
word_len = len(words[0])
word_count = len(words)
total_len = word_len * word_count
# 目标是找到每个长度为total_len的子串是否可以由words中的单词组成
word_dict = Counter(words) # 记录每个单词出现的次数
result = []
# 遍历整个字符串,滑动窗口的起始位置可以从0到len(s)-total_len
for i in range(word_len):
left = i
right = i
current_count = 0
current_dict = Counter()
while right + word_len <= len(s):
# 取出窗口中的当前单词
word = s[right:right + word_len]
right += word_len
# 如果当前单词在words列表中
if word in word_dict:
current_dict[word] += 1
current_count += 1
# 如果单词的出现次数超过了字典中的次数,说明不符合要求
while current_dict[word] > word_dict[word]:
left_word = s[left:left + word_len]
current_dict[left_word] -= 1
left += word_len
current_count -= 1
# 如果当前窗口中包含所有单词,记录位置
if current_count == word_count:
result.append(left)
else:
# 如果当前单词不在words列表中,重新开始滑动窗口
current_dict.clear()
current_count = 0
left = right
return result
代码解析
这段代码的核心思想其实就是滑动窗口(Sliding Window)的应用。具体来说:
计数器:我们首先用 Counter
来记录words
中每个单词出现的次数,便于后续快速对比。滑动窗口:窗口的大小是 word_len * word_count
,也就是words
中所有单词的总长度。我们从字符串s
中依次滑动这个窗口,并在每次滑动时检查当前窗口中的单词是否符合条件。滑动窗口的内外两层循环:外层循环从0到 word_len
,因为窗口的起始位置是有周期性的。内层循环用于扩展右边界,直到窗口中的单词总数满足条件,或者遇到不在words
中的单词。
从复杂度上来看,这段代码的时间复杂度大概是 O(n * m),其中 n
是字符串 s
的长度,m
是单词列表 words
中的单词数。虽然这已经比较高效了,但还是可以通过一些细节优化来提高性能,比如提前判断 s
长度和 words
的关系,或者在实际应用中进一步优化内存使用。
说到底,写代码就像是拼拼图,得有个大致的框架和思路,然后每一步都是在搭建。只不过拼到最后的作品,往往更有成就感。
对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》。