1. 无重复字符的最长子串问题解析
无重复字符的最长子串(Longest Substring Without Repeating Characters)是字符串处理中的经典问题,也是技术面试中的高频考题。给定一个字符串,我们需要找到其中不含有重复字符的最长子串的长度。
1.1 问题定义与示例
假设我们有一个字符串 "abcabcbb",那么最长的无重复字符子串是 "abc",其长度为3。另一个例子是 "bbbbb",最长子串就是 "b" 本身,长度为1。
这个问题看似简单,但要高效解决需要考虑多种边界情况:
- 空字符串的情况
- 全相同字符的字符串
- 包含各种Unicode字符的情况
- 大小写是否敏感(通常题目会说明)
1.2 暴力解法分析
最直观的解法是暴力枚举所有可能的子串,然后检查是否有重复字符:
python复制def lengthOfLongestSubstring(s: str) -> int:
n = len(s)
ans = 0
for i in range(n):
for j in range(i+1, n+1):
if allUnique(s, i, j):
ans = max(ans, j-i)
return ans
def allUnique(s, start, end):
chars = set()
for i in range(start, end):
if s[i] in chars:
return False
chars.add(s[i])
return True
这种方法的时间复杂度是O(n³),空间复杂度是O(min(n,m)),其中m是字符集大小。显然这在处理长字符串时效率极低。
1.3 滑动窗口优化
更高效的解法是使用滑动窗口技术。滑动窗口是处理数组/字符串子区间问题的常用技巧,通过维护一个窗口,在满足条件时扩展窗口,不满足时收缩窗口。
python复制def lengthOfLongestSubstring(s: str) -> int:
n = len(s)
ans = 0
char_index = {} # 存储字符最后出现的位置
left = 0
for right in range(n):
if s[right] in char_index:
left = max(char_index[s[right]] + 1, left)
ans = max(ans, right - left + 1)
char_index[s[right]] = right
return ans
这个算法的时间复杂度降到了O(n),空间复杂度仍然是O(min(n,m))。关键在于当遇到重复字符时,如何快速调整窗口的左边界。
提示:在实际面试中,建议先解释暴力解法,再提出优化思路,最后给出滑动窗口的实现。这种循序渐进的方式能展示你的问题解决能力。
1.4 边界情况处理
在实际编码中,有几个边界情况需要特别注意:
- 空字符串输入应返回0
- 字符串中可能包含空格、数字或特殊字符
- Unicode字符的处理(Python3中字符串默认是Unicode)
- 大小写是否视为相同字符(通常题目会说明)
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 带冷却期的股票最大收益问题
Maximum Profit with Cooldown(带冷却期的股票最大收益)是动态规划在金融问题中的一个典型应用。给定一个股票价格数组,其中第i个元素是第i天的股票价格。设计算法计算最大收益,交易规则如下:
- 可以完成任意次交易
- 卖出股票后有一天冷却期不能买入
- 不能同时参与多笔交易(必须在再次购买前卖出之前的股票)
2.1 问题建模与状态定义
这类问题通常用动态规划解决,关键在于正确定义状态。我们可以定义三种状态:
- hold[i]:第i天结束时持有股票的最大收益
- sold[i]:第i天结束时刚卖出股票的最大收益
- rest[i]:第i天结束时处于冷却期或不持有股票的最大收益
状态转移方程为:
- hold[i] = max(hold[i-1], rest[i-1] - prices[i])
- sold[i] = hold[i-1] + prices[i]
- rest[i] = max(rest[i-1], sold[i-1])
最终结果是max(sold[n-1], rest[n-1]),因为最后一天持有股票没有意义。
2.2 动态规划实现
python复制def maxProfit(prices):
if not prices:
return 0
n = len(prices)
hold = [0] * n
sold = [0] * n
rest = [0] * n
hold[0] = -prices[0]
for i in range(1, n):
hold[i] = max(hold[i-1], rest[i-1] - prices[i])
sold[i] = hold[i-1] + prices[i]
rest[i] = max(rest[i-1], sold[i-1])
return max(sold[-1], rest[-1])
这个解法的时间复杂度是O(n),空间复杂度也是O(n)。可以通过观察发现,每一天的状态只依赖于前一天的状态,因此可以优化空间复杂度到O(1)。
2.3 空间优化版本
python复制def maxProfit(prices):
if not prices:
return 0
hold = -prices[0]
sold = 0
rest = 0
for i in range(1, len(prices)):
prev_hold = hold
hold = max(hold, rest - prices[i])
rest = max(rest, sold)
sold = prev_hold + prices[i]
return max(sold, rest)
注意:这类动态规划问题容易混淆状态转移的条件。建议在纸上画出状态转移图,明确每个状态的来源和去向。
3. 两个问题的对比与联系
虽然这两个问题看似不同,一个来自字符串处理,一个来自动态规划,但它们都体现了算法设计中的一些共通思想:
3.1 滑动窗口与状态机
无重复字符问题中的滑动窗口可以看作是一种特殊的状态机,窗口内的字符集合就是状态。而股票问题中的三种状态(hold、sold、rest)也是状态机的体现。
3.2 时间复杂度优化
两个问题都经历了从暴力解法到优化解法的过程:
- 无重复字符:从O(n³)到O(n)
- 股票问题:从O(n²)的递归到O(n)的DP
这种优化思路在解决其他问题时也很有借鉴意义。
3.3 实际应用场景
这两个问题都有实际应用价值:
- 无重复字符子串可用于文本分析、密码学等领域
- 带冷却期的股票交易模型直接对应现实中的交易策略
4. 解题技巧与常见错误
4.1 无重复字符问题的陷阱
- 忽略字符大小写:题目是否说明大小写敏感?
- 错误处理空字符串:总是先检查输入是否为空
- 滑动窗口边界更新错误:left = max(...)而不是直接赋值
- 使用数组而非哈希表记录位置:对于Unicode字符集效率低
4.2 股票问题的易错点
- 状态定义不完整:漏掉rest状态
- 冷却期处理错误:必须在卖出后隔一天才能买
- 初始化错误:hold[0]应为-prices[0]
- 空间优化时变量覆盖问题:需要临时保存前一个值
4.3 调试建议
对于这类问题,建议:
- 先手动计算小测试用例
- 打印中间状态变量(如滑动窗口的左右边界、DP表)
- 画状态转移图理清逻辑
- 考虑极端情况(空输入、单元素、全相同元素等)
我在实际面试和编码中发现,很多同学能够写出大致正确的代码,但在边界条件和状态转移上容易出错。建议在写完代码后,专门花时间检查这些关键点。
