滑动窗口与动态规划:解决最长无重复子串与股票买卖问题

发布时间:2026/11/4 7:51:01
滑动窗口与动态规划:解决最长无重复子串与股票买卖问题 1. 最长无重复字符子串问题解析1.1 问题描述与直观理解给定一个由ASCII字符组成的字符串s我们需要找到其中最长的连续子串该子串中不能包含重复字符。例如对于字符串abcabcbb最长无重复字符子串是abc长度为3。这个问题看似简单但要在O(n)时间复杂度内解决需要一些技巧。最直观的暴力解法是检查所有可能的子串判断是否有重复字符然后记录最大长度。这种方法的时间复杂度是O(n²)对于长字符串效率太低。1.2 滑动窗口算法原理滑动窗口算法是解决这类子串/子数组问题的利器。其核心思想是维护一个窗口[l,r]通过调整窗口边界来寻找符合条件的子串。具体到这个题目我们使用两个指针l和r表示窗口的左右边界使用哈希表记录每个字符最后一次出现的位置当遇到重复字符时将左边界l移动到max(l, 重复字符上次出现位置1)每次移动右边界r时更新最大长度这种方法的精妙之处在于它保证了每个字符最多被访问两次一次加入窗口一次移出窗口因此时间复杂度是严格的O(n)。1.3 代码实现与逐行解析class Solution { public: int lengthOfLongestSubstring(string s) { unordered_mapchar, int lastPos; // 记录字符最后出现的位置 int maxLen 0; for(int r 0, l 0; r s.size(); r) { // 如果当前字符已经存在于窗口中更新左边界 if(lastPos.find(s[r]) ! lastPos.end()) { l max(l, lastPos[s[r]] 1); } // 更新最大长度 maxLen max(maxLen, r - l 1); // 记录当前字符的位置 lastPos[s[r]] r; } return maxLen; } };关键点解析lastPos哈希表存储每个字符最后出现的位置当发现重复字符时l max(l, lastPos[s[r]] 1)确保窗口不会回退maxLen始终记录着当前找到的最大长度1.4 算法优化与变种对于ASCII字符集我们可以用固定大小的数组替代哈希表进一步提升效率int lengthOfLongestSubstring(string s) { vectorint charIndex(128, -1); // ASCII字符共128个 int maxLen 0, l 0; for(int r 0; r s.size(); r) { if(charIndex[s[r]] l) { l charIndex[s[r]] 1; } charIndex[s[r]] r; maxLen max(maxLen, r - l 1); } return maxLen; }这种实现将时间复杂度常数因子降得更低实际运行速度更快。1.5 常见错误与调试技巧边界条件处理空字符串、全相同字符的字符串等特殊情况需要测试窗口左边界回退确保左边界不会向左移动只能向右移动哈希表更新时机必须在判断重复之后更新字符位置初始值设置l和maxLen初始值应为0调试时可以打印窗口变化过程cout Window [ l , r ]: s.substr(l, r-l1) endl;2. 带冷却期的股票买卖问题解析2.1 问题描述与状态定义给定一个股票价格数组prices我们可以进行多次买卖但有如下限制卖出后必须冷却一天不能立即买入任何时候最多持有一股我们需要找出可以获得的最大利润。这个问题比常规的股票买卖问题更复杂因为存在状态转移的限制。我们需要明确定义每天可能处于的状态持有(hold)当天持有股票卖出(sold)当天卖出股票冷却(cool)当天处于冷却期前一天卖出了股票2.2 动态规划状态转移分析我们可以用动态规划来解决这个问题定义三个状态数组hold[i]第i天结束时持有股票的最大利润sold[i]第i天结束时刚卖出股票的最大利润cool[i]第i天结束时处于冷却期的最大利润状态转移方程hold[i]可以来自前一天就持有股票今天继续持有hold[i-1]前一天是冷却期今天买入股票cool[i-1] - prices[i]sold[i]只能来自前一天持有股票今天卖出hold[i-1] prices[i]cool[i]可以来自前一天就是冷却期今天继续冷却cool[i-1]前一天卖出股票今天必须冷却sold[i-1]2.3 两种视角的代码实现2.3.1 现金视角实现class Solution { public: int maxProfit(vectorint prices) { if(prices.empty()) return 0; int n prices.size(); vectorint hold(n), sold(n), cool(n); // 初始状态 hold[0] -prices[0]; // 第一天买入 sold[0] 0; // 第一天不能卖出 cool[0] 0; // 第一天不能冷却 for(int i 1; i n; i) { hold[i] max(hold[i-1], cool[i-1] - prices[i]); sold[i] hold[i-1] prices[i]; cool[i] max(cool[i-1], sold[i-1]); } return max(sold[n-1], cool[n-1]); } };2.3.2 资产视角实现class Solution { public: int maxProfit(vectorint prices) { if(prices.empty()) return 0; int n prices.size(); vectorint hold(n), sold(n), cool(n); // 初始状态 hold[0] 0; // 初始资产为0 sold[0] 0; // 第一天不能卖出 cool[0] 0; // 第一天不能冷却 for(int i 1; i n; i) { // 持有股票要么继续持有要么从冷却期买入 hold[i] max(hold[i-1] prices[i] - prices[i-1], cool[i-1]); // 卖出股票必须从持有状态卖出 sold[i] hold[i-1] prices[i] - prices[i-1]; // 冷却期要么继续冷却要么来自卖出 cool[i] max(cool[i-1], sold[i-1]); } return max(sold[n-1], cool[n-1]); } };2.4 状态压缩优化注意到每个状态只依赖于前一个状态我们可以将空间复杂度从O(n)优化到O(1)int maxProfit(vectorint prices) { int hold INT_MIN, sold 0, cool 0; for(int price : prices) { int prevHold hold; int prevSold sold; int prevCool cool; hold max(prevHold, prevCool - price); sold prevHold price; cool max(prevCool, prevSold); } return max(sold, cool); }2.5 实际应用与变种问题这类问题在实际量化交易中有广泛应用。理解状态转移对于设计交易策略至关重要。类似的问题变种包括交易手续费每次交易收取固定费用交易次数限制最多进行k次交易多重冷却期卖出后需要冷却多日3. 算法实战经验分享3.1 滑动窗口算法的应用场景滑动窗口算法不仅适用于最长无重复子串问题还适用于最小覆盖子串长度最小的子数组最多包含K个不同字符的子串字符串排列关键识别特征是需要在数组/字符串中寻找满足特定条件的连续子序列。3.2 动态规划状态设计的技巧对于复杂的动态规划问题设计合适的状态是关键。我的经验是明确问题的所有可能状态确定状态之间的转移条件考虑初始状态和边界条件尝试用状态机表示转移关系必要时画出状态转移图3.3 调试复杂算法的实用方法打印中间状态在关键步骤打印变量值小规模测试先用简单例子验证边界测试空输入、单元素输入等可视化工具使用调试器逐步执行对比验证用暴力解法验证正确性3.4 性能优化实践在实际编码面试中除了正确性还需要考虑时间复杂度分析空间复杂度优化常数因子优化如用数组替代哈希表避免不必要的计算利用问题特性进行剪枝对于最长无重复子串问题我实测发现数组实现比哈希表实现快约30%。在极端情况下如超长字符串这种优化可能决定是否通过测试用例。4. 常见问题解答4.1 最长无重复子串问题Q为什么滑动窗口算法的时间复杂度是O(n)A每个字符最多被访问两次一次加入窗口一次移出窗口所以总操作次数是2n时间复杂度为O(n)。Q如何处理Unicode字符而不仅是ASCIIA可以将哈希表改为支持Unicode的类型如unordered_mapwchar_t, int算法逻辑不变。Q如果需要输出最长子串本身而不仅是长度怎么办A在更新最大长度时同时记录窗口的起始和结束位置最后用substr提取即可。4.2 带冷却期的股票买卖问题Q为什么最后的结果是max(sold[n-1], cool[n-1])A因为最后一天持有股票没有意义无法获利所以只考虑卖出或冷却状态。Q如何扩展这个问题到允许最多k次交易A需要增加一个维度表示交易次数状态转移会更复杂但基本思路类似。Q现金视角和资产视角有什么区别A现金视角跟踪实际现金流动资产视角跟踪相对收益。现金视角更直观资产视角有时能简化计算。Q如何处理交易手续费的情况A在买入或卖出时扣除手续费即可例如sold[i] hold[i-1] prices[i] - fee。在实际面试中理解这些算法的核心思想比死记硬背代码更重要。我建议多练习类似的题目培养对问题模式的敏感度和状态设计的能力。对于动态规划问题画出状态转移图往往能帮助理清思路。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询