在過去幾天中,我們分別學習了「約束與邊界分析」、「Big-O 複雜度評估」、「雙指針思維」以及「數學與邊界防禦」。今天作為演算法模組的收官篇,我們要將這些方法論融會貫通。
今天我們挑選 LeetCode 上極為經典的高頻 Medium 題目——No. 3: Longest Substring Without Repeating Characters(無重複字元的最長子串)。我們將完整記錄從「卡關、提出需求、AI 給出解法、挖掘潛在 Bug、最終極限優化」的人機協作實錄!
題目描述:給定一個字串 s,請找出其中不含有重複字元的最長子串的長度。
範例:s = "pwwkew",最長無重複子串是 "wke",長度為 3。
初學者最容易想到的方式是「以每一個字元為起點,往後擴展檢查重複」,時間複雜度為 O(n²)。我們向 AI 提問:
【提問 Prompt】
「我正在解 LeetCode 3,暴力列舉子串的時間複雜度是 O(n²)。請問這題在資料結構上有哪種標準模型可以在 O(n) 時間內完成?」
【AI 的回答要點】
AI 指出:本題的最佳模型是**「滑動窗口(Sliding Window)」**。
[left, right],表示目前「沒有重複字元」的子字串。我們請 AI 產出第一版基於 HashSet 的滑動窗口代碼:
[第一版:基於 Set 的滑動窗口]
import java.util.HashSet;
import java.util.Set;
public class SolutionV1 {
public int lengthOfLongestSubstring(String s) {
if (s == null || s.length() == 0) return 0;
Set<Character> window = new HashSet<>();
int left = 0, maxLength = 0;
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
// 遇到重複字元,左指針一步一步往右移,直到刪除重複者
while (window.contains(c)) {
window.remove(s.charAt(left));
left++;
}
window.add(c);
maxLength = Math.max(maxLength, right - left + 1);
}
return maxLength;
}
}
雖然這版已經可以在 O(2n) = O(n) 時間內通過,但我們讓 AI 進行 Code Review:
「如果字串很長,左指針用 while 一步步移動效率稍慢。例如遇到 abba 這種情況,能不能讓左指針直接『瞬移』到重複字元的下一位?」
經過討論,AI 提供了更進階的優化方案:
Map<Character, Integer> 記錄「字元最後出現的索引位置」。當發現重複時,左指針直接 left = Math.max(left, map.get(c) + 1) 跳躍過去!int[] 取代 HashMap,免除 Java 物件裝箱(Autoboxing)與 Hash 碰撞開銷。[最終最優 Java 實作]
public class Solution {
public int lengthOfLongestSubstring(String s) {
if (s == null || s.length() == 0) {
return 0;
}
// 用 ASCII 陣列記錄每個字元上一次出現的索引 + 1(預設為 0 代表尚未出現)
int[] lastIndex = new int[128];
int maxLength = 0;
int left = 0;
for (int right = 0; right < s.length(); right++) {
char currentChar = s.charAt(right);
// 若該字元先前出現過,且位置在當前窗口左側邊界之後,直接跳躍左邊界
left = Math.max(left, lastIndex[currentChar]);
// 計算當前無重複窗口長度並更新最大值
maxLength = Math.max(maxLength, right - left + 1);
// 記錄該字元下次出現時,left 應該跳到的位置(即 right + 1)
lastIndex[currentChar] = right + 1;
}
return maxLength;
}
}
回顧這一題的攻克過程,人機協作帶來了三項實質優勢:
Math.max(left, ...),導致左指針不小心往左倒退走。AI 在解釋時特別指出了「避免左指針走回頭路」的邊界陷阱。HashSet 一步步收斂到 int[128] 陣列查表,記憶體消耗維持常數 O(1),執行時間達到 LeetCode 擊敗 99% 的水準。今天我們透過完整的對話實錄,示範了如何與 AI 深度協作,將一道 Medium 演算法題從粗胚原型一步步推進至最優解法。到今天為止,第二模組(演算法與資料結構)正式圓滿落幕!