← 返回部落格列表 Persona OA 2026 真題解析:CodeSignal 記憶體資料庫 Level 1-4 完整通關|一畝三分地
Persona

Persona OA 2026 真題解析:CodeSignal 記憶體資料庫 Level 1-4 完整通關|一畝三分地

2026-05-15

Persona Identities 作為身分驗證賽道的明星 Y Combinator 公司,2026 年釋出大量 Backend / Full-stack 職缺。它的 OA 不走 LeetCode 路線,而是用 CodeSignal 的「四級漸進題」——一個題面隨 Level 加碼,前一關的程式碼必須能相容後一關。最常見的就是這道**「In-Memory Database」**,幾乎已是 Persona OA 的「招牌題」,類似 Ramp、Square 也採用過相同框架。

本篇基於一畝三分地最新的 Persona OA 複盤,把 Level 1-4 完整拆開,給出可以一路遞增重構的 Python 實作,並提醒每個 Level 容易「掛掉」的坑。

Persona OA 概覽

維度 詳情
平台 CodeSignal General Coding Framework
時長 90 分鐘(含 IDE 熟悉時間)
題量 1 道大題 × 4 個 Level
測試用例 累計約 25-30 個
語言 Python / Java / Go / JS 均可,但 Python 通過率最高
滿分 100 分(L1=20、L2=25、L3=25、L4=30)

CodeSignal 的特殊之處:每個 Level 只暴露當級測試,下一級要等通過當前級才能解鎖。Level 1 如果把欄位型別寫死,到 Level 3 會被逼著大改。

Level 1:基礎 Key-Value 操作

題目描述

實作記憶體資料庫,支援:

所有回傳值都是字串。

解題思路

天然兩層 dict:db[key][field] = value。重點是「key 不存在時不要預先建立空 dict」——否則 Level 4 會污染備份。

Python 解法

class InMemoryDB:
    def __init__(self):
        self.db = {}

    def set(self, key, field, value):
        self.db.setdefault(key, {})[field] = value
        return ""

    def get(self, key, field):
        return self.db.get(key, {}).get(field, "")

    def delete(self, key, field):
        if key in self.db and field in self.db[key]:
            del self.db[key][field]
            if not self.db[key]:
                del self.db[key]
            return "true"
        return "false"

時間複雜度:均攤 O(1) 空間複雜度:O(K·F)

Level 2:掃描與前綴查詢

題目描述

新增:

解題思路

直接 sorted(items())不要用 SortedDict——Level 4 會做批次回滾,越簡單越好維護。

Python 解法

    def scan(self, key):
        if key not in self.db:
            return ""
        items = sorted(self.db[key].items())
        return ", ".join(f"{f}({v})" for f, v in items)

    def scan_by_prefix(self, key, prefix):
        if key not in self.db:
            return ""
        items = sorted(
            (f, v) for f, v in self.db[key].items() if f.startswith(prefix)
        )
        return ", ".join(f"{f}({v})" for f, v in items)

時間複雜度:O(F log F)

Level 3:TTL 過期機制

題目描述

每個命令都帶時間戳:

判定嚴格ts >= expire_at 即過期。

解題思路

把 value 改成 (value, expire_at)None 代表永久。所有讀取路徑都過同一個 helper _alive(key, field, ts),避免在 6 個方法裡散落判斷。

Python 解法

class InMemoryDB:
    def __init__(self):
        self.db = {}

    def _alive(self, key, field, ts):
        rec = self.db.get(key, {}).get(field)
        if rec is None:
            return None
        value, expire_at = rec
        if expire_at is not None and ts >= expire_at:
            return None
        return value

    def set_at(self, key, field, value, ts):
        self.db.setdefault(key, {})[field] = (value, None)
        return ""

    def set_at_with_ttl(self, key, field, value, ts, ttl):
        self.db.setdefault(key, {})[field] = (value, ts + ttl)
        return ""

    def get_at(self, key, field, ts):
        v = self._alive(key, field, ts)
        return "" if v is None else v

    def delete_at(self, key, field, ts):
        if self._alive(key, field, ts) is None:
            return "false"
        del self.db[key][field]
        return "true"

    def scan_at(self, key, ts):
        if key not in self.db:
            return ""
        live = sorted(
            (f, v) for f, (v, exp) in self.db[key].items()
            if exp is None or ts < exp
        )
        return ", ".join(f"{f}({v})" for f, v in live)

陷阱:很多人主動刪除已過期項——這會讓 Level 4 的「指定時間點還原」遺失資料。**延遲過期(lazy expire)**才是正確做法。

Level 4:備份與還原(Backup / Restore)

題目描述

解題思路

兩個關鍵:

  1. 快照存什麼:不能用淺拷貝。要存 (field, value, remaining_ttl),其中 remaining_ttl = expire_at - backup_ts
  2. 還原時改寫 expire_atnew_expire = ts + remaining_ttl

很多人栽在第 2 步——直接拷回舊 expire_at,結果還原後 TTL 立刻過期。

Python 解法

class InMemoryDB:
    def __init__(self):
        self.db = {}
        self.backups = {}

    # ... Level 1-3 方法沿用 ...

    def backup(self, ts):
        snapshot = {}
        for key, fields in self.db.items():
            live = {}
            for f, (v, exp) in fields.items():
                if exp is None:
                    live[f] = (v, None)
                elif ts < exp:
                    live[f] = (v, exp - ts)
            if live:
                snapshot[key] = live
        self.backups[ts] = snapshot
        return str(len(snapshot))

    def restore(self, ts, backup_ts):
        if backup_ts not in self.backups:
            return ""
        snap = self.backups[backup_ts]
        new_db = {}
        for key, fields in snap.items():
            new_fields = {}
            for f, (v, remaining) in fields.items():
                exp = None if remaining is None else ts + remaining
                new_fields[f] = (v, exp)
            new_db[key] = new_fields
        self.db = new_db
        return ""

Level 4 常見錯誤

  1. copy.deepcopy(self.db) 當快照——會把已過期資料帶回
  2. 還原時沒重算 expire_at——TTL 全部失效
  3. SET_AT 反向動到 backup——backup 必須唯讀
  4. 過濾過期 + 計算剩餘做兩遍——合併成單次遍歷

Persona 備考策略

階段 推薦練習
熟悉 CodeSignal LC 146 LRU Cache、LC 460 LFU Cache
狀態機 LC 432、LC 1206 Skiplist
TTL 思維 Redis EXPIRE 文件 + LC 362 Hit Counter
備份還原 git 增量快照思路、LC 1166 File System

節奏建議:Level 1 控制在 15 分鐘內滿分、Level 2-3 各 20-25 分鐘、Level 4 保留 25-30 分鐘。Level 1 不要過度抽象——CodeSignal 只看測試通過率。


FAQ

Q1:Persona OA 難度怎麼樣?比 LeetCode Medium 難嗎?

單題不超過 LC Medium,但 Persona 考的是漸進式重構能力——四個 Level 累計 90 分鐘要相容無縫銜接。Level 1 寫死型別,Level 3 會很痛苦。

Q2:Persona OA 用什麼平台?多長時間?

CodeSignal General Coding Framework,90 分鐘。注意:之前還會有一個 60 分鐘的 GCA 預篩,那才是純 LeetCode 題型,In-Memory Database 是 GCA 之後的 Practical Task。

Q3:Backend 和 Full-stack 的 OA 一樣嗎?

OA 完全一致。Full-stack 在 OA 後多一輪 React/TypeScript 實戰,後端進入系統設計。OA 階段不必區分。

Q4:哪種語言通過率最高?

Python > Go > Java > JS。CodeSignal 的 Python 範本和巢狀 dict 寫法最省時間。避免用 Java,泛型語法會吃掉大量時間。

Q5:Level 4 的 backup_ts 如果不存在怎麼辦?

題目保證 backup_ts 之前 BACKUP 過——但多次 BACKUP 後 RESTORE 較早的那個是常見隱藏 case,要用 dict 不要用 list。

Q6:OA 通過後多久收到下一輪通知?

一畝三分地多數反饋 3-7 個工作日。Persona 流程節奏快,hiring manager 經常直接寄行事曆邀請,注意檢查垃圾信件夾。


正在準備 Persona / Ramp / Square 的 CodeSignal 系列 OA?

CodeSignal 四級題考的是工程化思維,不是演算法天花板。若想取得我們整理的同類公司題庫(Persona、Ramp、Stripe、Square、Brex),或希望針對 Level 3-4 狀態機做 1v1 複盤,歡迎聯繫。

加微信 Coding0201取得 CodeSignal 四級題完整題庫

聯絡方式

Email: [email protected] Telegram: @OAVOProxy