程序员老鬼

表弟是某米员工,月薪23K,工作第二年,年薪30万国企女友结婚要100多万,给不起,分手了。。。

哎,这年头程序员不仅要会写代码,还得能扛生活的锅。

某位网友说他表弟在某米上班,月薪23K,年薪加加班加奖金差不多30W,工作才第二年,算是挺不错的了。但偏偏女朋友是国企的,说结婚得花一百多万,结果小伙子一听这预算,直接分了。

Image

我觉得吧,这年头搞对象像开源项目,你得先看自己是不是能长期维护。月薪23K听起来不错,但掏空六个钱包去堆一个婚礼系统,属实是性能过载。程序员就怕的就是这个——“需求变更还得今天上线”。

也不是说不肯花钱,只是我们天天优化代码性能,理财思维也是写进脑子里的。房子、彩礼、装修、车位、婚宴……一行行费用看着像 debug 报错,真心顶不住。

爱情这事啊,归根结底还得两人一个版本,才好同步升级,不然光有热情,资源都烧干了也跑不动。【备注:文末可领最新资料】。

算法题:串联所有单词的子串

最近刷 LeetCode 刷到一道题,题目名挺有诗意的:《串联所有单词的子串》,一看就不像能摸鱼混过去的。果不其然,这题不光长得像 Hard,内心也很 Hard,真不是靠两行 for 循环就能拿下的类型 。
题目是这么说的:给一个字符串 s 和一个字符串数组 words,所有单词长度一样,要求找出 s 中所有起始索引位置,这些位置开始的子串刚好是由 words 中所有单词拼接而成,中间不能有任何多余字符,顺序可以随意。
比如:
s = "barfoothefoobarman";words = ["foo","bar"];// 返回 [0,9]
一开始我还天真地想着是不是可以暴力枚举所有子串,然后检查是不是包含所有的 word。结果一跑,数据量一上去直接 TLE,LeetCode 服务器差点把我封号了 🙃。
那怎么办?暴力肯定不行,得动点脑子。
这题的关键在于所有单词长度一样。这信息不能浪费。假设每个单词长度是 wordLen,words 总长度是 wordLen * words.length,那我们完全可以每次只考虑这段长度的窗口,然后用滑动窗口和哈希表来检查窗口里的内容是不是符合要求。
贴个我觉得比较清晰的实现(别怕,看着长其实逻辑还蛮清楚的):
public List<Integer> findSubstring(String s, String[] words) {    List<Integer> res = new ArrayList<>();    if (s == null || s.length() == 0 || words == null || words.length == 0) return res;
int wordLen = words[0].length(); int totalLen = wordLen * words.length;
// 构造 words 的频率表 Map<String, Integer> wordMap = new HashMap<>(); for (String word : words) { wordMap.put(word, wordMap.getOrDefault(word, 0) + 1); }
// 一共 wordLen 种不同的起始偏移(滑动窗口分组) for (int i = 0; i < wordLen; i++) { int left = i, right = i; int count = 0; Map<String, Integer> windowMap = new HashMap<>();
while (right + wordLen <= s.length()) { String w = s.substring(right, right + wordLen); right += wordLen;
if (wordMap.containsKey(w)) { windowMap.put(w, windowMap.getOrDefault(w, 0) + 1); count++;
// 超过了预期数量,就要收缩窗口 while (windowMap.get(w) > wordMap.get(w)) { String leftWord = s.substring(left, left + wordLen); windowMap.put(leftWord, windowMap.get(leftWord) - 1); left += wordLen; count--; }
if (count == words.length) { res.add(left); } } else { // 碰到无效词,窗口直接清空 windowMap.clear(); count = 0; left = right; } } }
return res;}
这段代码的核心是用滑动窗口搭配多个起始偏移,把整个字符串分批次“咬”下来检查。而不是老老实实一个字符一个字符地扫,效率那是蹭蹭地上来了,刷起来倍儿爽。
我最喜欢这个逻辑的一点是“窗口自动滑动+频率控制”,就跟程序员日常控制内存和线程池一样,啥都得精准,稍微多了点都得收回来 。
当然了,也有很多朋友看到这种题直接就心态崩了:“为啥算法题要搞得像玩扫雷一样复杂?”但说实话,搞懂这种滑窗+哈希的经典套路,不只是刷题用,写服务的时候你优化日志匹配、窗口控制、数据抽取,其实都能派上用场。
我以前在项目里搞过一段字段解析逻辑,就是把日志中类似 "user_action_login_register_logout" 的数据按规则拆成关键词组合找频次,结果你别说,还真是这题的变种思路救了我。老板当时还夸我“思路清奇”,我当时只想说一句:“LeetCode 背后有人,真的。”
所以别看题目名字像考古,其实实用性拉满,技巧纯纯的硬核,Java写起来也挺顺手。你要是下次刷到类似题,别怕,默念一遍:滑动窗口 + 哈希表,干它!
顺便偷偷加一句:如果你能理解这个滑动窗口写法,再去看“最小覆盖子串”、“无重复最长子串”之类的,思路会越来越顺,效率也能像 JDK17 一样飞起 。

最后,我为大家打造了一份deepseek的入门到精通教程,完全免费:https://www.songshuhezi.com/deepseek

也可以看我写的这篇文章《DeepSeek满血复活,直接起飞!》来进行本地搭建。

-END-

ok,今天先说到这,老规矩,给大家分享一份不错的副业资料,感兴趣的同学可以链接我,微信:hls404 找我领取。

以上,就是今天的分享了,看完文章记得右下角点赞,也欢迎在评论区写下你的留言。