iT邦幫忙

2026 iThome 鐵人賽

DAY 2
0
ChatGPT & Codex

從 Prompt Engineering 到 Copilot / Codex 實務 30 天系列 第 11 篇

Day 11:【實戰紀錄】攻克經典 Medium 題目:與 AI 協同解題、除錯與優化的完整思維過程

  • 分享至 

  • xImage
  •  

在過去幾天中,我們分別學習了「約束與邊界分析」、「Big-O 複雜度評估」、「雙指針思維」以及「數學與邊界防禦」。今天作為演算法模組的收官篇,我們要將這些方法論融會貫通。

今天我們挑選 LeetCode 上極為經典的高頻 Medium 題目——No. 3: Longest Substring Without Repeating Characters(無重複字元的最長子串)。我們將完整記錄從「卡關、提出需求、AI 給出解法、挖掘潛在 Bug、最終極限優化」的人機協作實錄!


一、題目與初始思維碰撞

題目描述:給定一個字串 s,請找出其中不含有重複字元的最長子串的長度。
範例:s = "pwwkew",最長無重複子串是 "wke",長度為 3。

步驟 1:向 AI 諮詢核心解題模型

初學者最容易想到的方式是「以每一個字元為起點,往後擴展檢查重複」,時間複雜度為 O(n²)。我們向 AI 提問:

【提問 Prompt】
「我正在解 LeetCode 3,暴力列舉子串的時間複雜度是 O(n²)。請問這題在資料結構上有哪種標準模型可以在 O(n) 時間內完成?」

【AI 的回答要點】
AI 指出:本題的最佳模型是**「滑動窗口(Sliding Window)」**。

  • 窗口本質:維護一個區間 [left, right],表示目前「沒有重複字元」的子字串。
  • 右指針(right):不斷向右擴展,嘗試將新字元納入窗口。
  • 左指針(left):一旦發現新字元在當前窗口內已經出現過,左指針必須「向右收縮」,直到窗口內排除重複字元為止。

二、第一版實作與除錯過程

我們請 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 這種情況,能不能讓左指針直接『瞬移』到重複字元的下一位?」


三、第二版極限優化:HashMap 索引跳躍與陣列查表

經過討論,AI 提供了更進階的優化方案:

  1. 直接跳躍:改用 Map<Character, Integer> 記錄「字元最後出現的索引位置」。當發現重複時,左指針直接 left = Math.max(left, map.get(c) + 1) 跳躍過去!
  2. 陣列取代 Hash:由於題目字元均為標準 ASCII(共 128 種可能),用長度 128 的 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;
}

}


四、人機協同刷題的心得複盤

回顧這一題的攻克過程,人機協作帶來了三項實質優勢:

  1. 避免卡在思維盲區:在我們只想得到 O(n²) 時,AI 迅速點出「滑動窗口」的核心模式,省去盲目摸索的時間。
  2. 指出關鍵邊界陷阱:在實作指針跳躍時,很多人會漏掉 Math.max(left, ...),導致左指針不小心往左倒退走。AI 在解釋時特別指出了「避免左指針走回頭路」的邊界陷阱。
  3. 極限工程優化:從 HashSet 一步步收斂到 int[128] 陣列查表,記憶體消耗維持常數 O(1),執行時間達到 LeetCode 擊敗 99% 的水準。

今日小結與預告

今天我們透過完整的對話實錄,示範了如何與 AI 深度協作,將一道 Medium 演算法題從粗胚原型一步步推進至最優解法。到今天為止,第二模組(演算法與資料結構)正式圓滿落幕!


上一篇
Day 10:【數學與邏輯題】以 AI 推導數論與模擬問題的底層邏輯與優化解法
下一篇
Day 12:【代碼抓漏】貼上 Error Stack Trace:讓 ChatGPT 扮演除錯專家快速定位例外與錯誤
系列文
從 Prompt Engineering 到 Copilot / Codex 實務 30 天 共 18 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言