題目描述:給定兩個字串 s 和 t,判斷 s 是否為 t 的子序列(subsequence)。子序列是指從 t 中刪去部分(或不刪)字元後,剩餘字元在不改變相對順序的情況下組成的字串。
解題思路:使用雙指標法。維護指標 i 指向 s、指標 j 指向 t,同時向右掃描 t。每當 t[j] == s[i] 時,代表 s 中的第 i 個字元在 t 中找到了對應位置,將 i 往右移一步;無論是否匹配,j 都持續往右推進。當 i 到達 s.size() 時,表示 s 的所有字元都在 t 中按順序出現,回傳 true;若 j 掃完 t 而 i 仍未到達 s.size(),則回傳 false。此法只需單次線性掃描,時間複雜度 O(n)。
時間複雜度:O(n) — 其中 n 為 t 的長度。指標 j 最多掃過 t 一次,i 最多前進 s.size() 次,整體為線性時間。
空間複雜度:O(1) — 只使用兩個整數指標,不需要額外的資料結構。
1. Initialize pointer i = 0 (for s) and j = 0 (for t). 2. While i < len(s) AND j < len(t): a. If s[i] == t[j]: increment i (character matched, advance in s). b. Increment j (always advance in t). 3. Return true if i == len(s) (all characters of s were matched), else false.
方法一:雙指標 O(n)(本題主要解法)
如上述解題思路,只需一次線性掃描 t,時間 O(n)、空間 O(1),最為簡潔高效。
方法二:動態規劃(處理大量查詢的進階變體)
若 t 固定不變、需對大量不同的 s 查詢(follow-up 情境),可預處理 t 建立二維表 next[j][c],表示位置 j 之後字元 c 下一次出現的位置。查詢每個 s 時只需 O(|s|) 時間跳轉而無需掃描整個 t。預處理時間 O(n * 26)、空間 O(n * 26),每次查詢 O(|s|)。
方法三:遞迴 O(n)
以遞迴方式實作:若 s 為空則回傳 true;若 t 為空而 s 不為空則回傳 false;若首字元相同則對 s[1:] 和 t[1:] 遞迴;否則對 s 和 t[1:] 遞迴。邏輯清晰但有函式呼叫堆疊開銷,不如雙指標迭代版本實用。
t 固定不變,需要對數十億個不同的 s 重複查詢是否為子序列,應如何優化?(提示:考慮預處理 t 的字元位置資訊)s 在 t 中匹配時每個字元對應的索引位置?s 的任意排列是否為 t 的子序列」,解法應如何調整?時間複雜度會如何變化?