← 返回部落格列表 TikTok OA CodeSignal 四道真題複盤與解法
TikTok

TikTok OA CodeSignal 四道真題複盤與解法

2026-08-13

前段時間投了 TikTok 的後端職缺,履歷過篩之後收到一封 CodeSignal 的測評邀請。這篇複盤記錄我在這場 OA 裡碰到的四道題、平台形式,以及每一題的完整解法。需要提一句:CodeSignal 最近把 General Coding Assessment 的介面刷新了一版,也輪換了一部分題目,但共享題庫裡的老題仍然會反覆出現——我這次四道裡就有三道是能在往期討論裡對上號的。如果你也在準備 TikTok 的 OA,或者想找 OA輔助 / OA代面 把節奏和思路一起打磨到位,希望這份筆記能幫到你。

面試環境與形式概覽

整場測評在 CodeSignal General Coding Assessment(共享題庫)上完成,全程限時 70 分鐘,一共四道題,難度由前往後遞進:前兩道是熱身性質的 Easy,第三題是矩陣/陣列模擬,第四題是偏資料結構的壓軸題。CodeSignal 會全程錄影、監控切換視窗,所以中途盡量別離開作答頁。

項目 詳情
平台 CodeSignal General Coding Assessment(共享題庫)
時長 70 分鐘
題量 4 道
難度 遞進(Easy → 模擬 → 資料結構)
考察重點 基礎實作速度、邊界處理、模擬與查詢效率

節奏建議:前兩道 Easy 盡量在 20 分鐘內拿下,把時間留給第三、四題。CodeSignal 的題目是要真正通過全部測試用例的,所以寫完記得自己先在腦子裡 dry run 幾個邊界例子再提交。


Problem 1:數字積減和

題目背景

給定一個正整數 n,計算它十進位各位數字的乘積與各位數字之和,回傳「乘積 − 和」。例如 123456:乘積是 1·2·3·4·5·6 = 720,和是 1+2+3+4+5+6 = 21,結果 720 − 21 = 699。再看 1010:因為包含數字 0,乘積直接變成 0,和是 0+1+0+1 = 2,結果 0 − 2 = -2

思路

把整數轉成字串再逐位轉回整數:list(map(int, str(n)))。一次走訪同時累乘和累加即可。乘積的初值設為 1,只要遇到任意一位是 0,乘積自然就變成 0,不需要單獨判斷。

Python 解法

def digit_product_minus_sum(n: int) -> int:
    """回傳 n 各位數字的乘積減去各位數字之和。

    只要有任意一位是 0,乘積會自然變為 0,無需特判。
    """
    digits = list(map(int, str(n)))  # 拆出每一位

    product = 1
    total = 0
    for d in digits:
        product *= d  # 累乘,遇到 0 會自然歸零
        total += d    # 累加

    return product - total

Dry run 演示

n = 123456 走一遍:

步驟 當前位 product total 說明
初始 1 0 product 初值為 1
讀 1 1 1 1
讀 2 2 2 3
讀 3 3 6 6
讀 4 4 24 10
讀 5 5 120 15
讀 6 6 720 21

最終 720 − 21 = 699

再驗證含 0 的邊界 n = 1010:走訪到第二位 0product 歸零,之後無論怎麼乘都還是 0,最終 total = 2,回傳 0 − 2 = -2,符合預期。

時間複雜度:O(log n),位數與 n 的對數成正比。 空間複雜度:O(log n),存放各位數字。


Problem 2:最長連續字元

題目背景

給定一個只含小寫字母的字串,找出被單個字元連續重複的最長一段;如果有多段並列最長,取最右邊的那一段;回傳「字元 + 長度」的拼接,例如連續三個 c 回傳 "c3"

思路

線性掃描,維護當前段的字元 cur_char 和長度 cur_len:遇到和上一位相同就 cur_len += 1,不同就把 cur_len 重設為 1 並更新 cur_char。同時用 best_len / best_char 記錄全域最佳。並列時取最右的關鍵是比較用 >=:只要出現同樣長的段,就用後來的覆蓋前面的。

Python 解法

def longest_run(s: str) -> str:
    """回傳最長連續重複字元段,並列時取最右邊一段。

    用 >= 保證相同長度時後出現的段覆蓋先出現的段。
    """
    if not s:
        return ""  # 空字串直接回傳空

    cur_char = s[0]
    cur_len = 1
    best_char = s[0]
    best_len = 1

    for ch in s[1:]:
        if ch == cur_char:
            cur_len += 1  # 延續當前段
        else:
            cur_char = ch  # 開啟新段
            cur_len = 1
        if cur_len >= best_len:  # >= 讓最右的並列段勝出
            best_len = cur_len
            best_char = cur_char

    return f"{best_char}{best_len}"

Dry run 演示

s = "aabbbxxccc"bbbccc 長度並列為 3)走一遍關鍵節點:

位置 字元 cur_char / cur_len best_char / best_len 說明
0 a a / 1 a / 1 初始
1 a a / 2 a / 2 延續
4 b b / 3 b / 3 第一段最長 3
7 c c / 1 b / 3 新開 c 段
9 c c / 3 c / 3 並列 3,>= 覆蓋為 c

最終回傳 "c3"bbbccc 都是長度 3,取最右的 ccc

時間複雜度:O(n),單次走訪。 空間複雜度:O(1),只用了常數個變數。


Problem 3:記憶體配置模擬

題目背景

模擬一段記憶體的配置與釋放。給定一系列操作:

思路

用一個布林陣列表示每個單元是否被佔用。alloc 時從左到右掃描,統計當前連續空閒長度,遇到被佔用的單元就把計數歸零;一旦計數達到 x,就把這段區間標記為佔用,並在雜湊表裡存下 id -> (start, length)erase 時用 ID 在雜湊表裡查到區間,把這些單元清空,然後刪除映射。

Python 解法

class MemoryRegion:
    """連續記憶體的配置 / 釋放模擬。

    used[i] 表示單元 i 是否被佔用;blocks 記錄 id -> (start, length)。
    """

    def __init__(self, size: int):
        self.used = [False] * size
        self.blocks = {}       # id -> (start, length)
        self.next_id = 0       # 遞增配置 ID

    def alloc(self, x: int) -> int:
        """佔用最左邊 x 個連續空閒單元,回傳起始索引;失敗回傳 -1。"""
        run = 0
        for i in range(len(self.used)):
            if self.used[i]:
                run = 0            # 碰到佔用,連續長度歸零
            else:
                run += 1
                if run == x:       # 湊夠 x 個連續空閒
                    start = i - x + 1
                    for j in range(start, i + 1):
                        self.used[j] = True
                    self.blocks[self.next_id] = (start, x)
                    self.next_id += 1
                    return start
        return -1                  # 沒有足夠的連續空閒

    def erase(self, block_id: int) -> int:
        """釋放該 ID 對應的整塊記憶體,回傳長度;無效 ID 回傳 -1。"""
        if block_id not in self.blocks:
            return -1
        start, length = self.blocks.pop(block_id)
        for j in range(start, start + length):
            self.used[j] = False
        return length

Dry run 演示

假設記憶體大小為 8,依次執行操作:

操作 記憶體狀態(1=佔用) 回傳 說明
初始 00000000 全空閒
alloc 3 11100000 0 最左 3 連空閒,配置 ID 0
alloc 2 11111000 3 接著往右,配置 ID 1
erase 0 00011000 3 釋放 ID 0 的 3 格
alloc 4 00011111 4 左側只有 3 空格不夠,落到索引 4
erase 5 00011111 -1 ID 5 不存在

時間複雜度alloc 為 O(n)(最壞掃描整段記憶體),erase 為 O(len)(清空對應區間)。 空間複雜度:O(n),用於佔用陣列與配置映射。


Problem 4:障礙區間查詢

題目背景

這一題在共享題庫裡的題面被貼錯了——原文把 Q4 配成了 Q1 的描述,真正的 Q4 要靠它的解法來還原。它實際考的是一個障礙放置 / 區間查詢問題:你維護一個有序的障礙位置陣列,支援兩種操作:

思路

bisect.insort 保持陣列有序,插入是 O(n)(陣列搬移導致)。查詢時用二分找到「位置小於等於 x - 1 的最靠右的那個障礙」,再看它是否落在 [x - size, x - 1] 內:落在裡面就說明放不下,回傳 False;否則回傳 True。查詢是 O(log n)。

提示:如果面試進一步要求插入也做到 O(log n),就把有序陣列換成平衡二元搜尋樹 / 有序集合(例如 sortedcontainers.SortedList),插入和查詢都能到 O(log n)。

Python 解法

import bisect


class ObstacleField:
    """維護有序障礙位置,支援新增與區間空閒查詢。"""

    def __init__(self):
        self.obstacles = []  # 始終保持升序

    def add(self, pos: int) -> None:
        """插入一個障礙,保持陣列有序(O(n) 的陣列搬移)。"""
        bisect.insort(self.obstacles, pos)

    def query(self, x: int, size: int) -> bool:
        """判斷 [x - size, x - 1] 區間內是否沒有障礙。"""
        left = x - size
        right = x - 1
        # 找到第一個 > right 的位置,它左邊一個就是 <= right 的最右障礙
        idx = bisect.bisect_right(self.obstacles, right) - 1
        if idx < 0:
            return True  # right 左側沒有任何障礙
        nearest = self.obstacles[idx]
        return nearest < left  # 落在 [left, right] 內則放不下

Dry run 演示

依次 ADD 2ADD 5ADD 9,陣列變為 [2, 5, 9],再做幾次查詢:

操作 目標區間 [x-size, x-1] 最近障礙 回傳 說明
QUERY(x=5, size=2) [3, 4] 2(<=4 最右) True 2 < 3,區間內無障礙
QUERY(x=6, size=3) [3, 5] 5 False 5 落在 [3,5]
QUERY(x=2, size=1) [1, 1] 無(idx<0 True 1 左側無障礙
QUERY(x=10, size=1) [9, 9] 9 False 9 落在 [9,9]

時間複雜度query 為 O(log n)(二分);add 為 O(n)(陣列搬移)。若改用有序集合,add 也可降到 O(log n)。 空間複雜度:O(n),存放障礙位置。


備考策略


FAQ

Q1:CodeSignal 的題目一定要通過全部測試用例嗎?

是的。和某些只看思路的現場輪不同,CodeSignal General Coding Assessment 是按通過的隱藏用例計分的,所以提交前一定要自己 dry run 幾個邊界例子,確認邏輯站得住再交。

Q2:共享題庫的老題現在還會出現嗎?

會。雖然介面刷新了一版、也輪換了部分題目,但共享題庫裡的經典題仍然高頻復現。我這次四道裡就有三道能在往期討論裡對上號,提前刷一遍高頻題很值。

Q3:Q3 記憶體模擬裡 alloc 找不到足夠空間怎麼處理?

掃描整段記憶體後連續空閒長度始終沒達到 x,就回傳 -1,且不改動任何狀態。注意每碰到一個被佔用的單元要立刻把連續計數歸零。

Q4:Q4 的題面被貼錯了,怎麼判斷真正要考什麼?

共享題庫偶爾會出現題面與測試用例不匹配的情況。遇到這種,以測試用例和函式簽章為準去反推真實邏輯——這一題的 ADD / QUERY 語意就是從用例還原出來的障礙區間查詢。

Q5:Q4 用有序陣列還是有序集合更好?

如果查詢遠多於插入,bisect + 有序陣列足夠,查詢 O(log n)、插入 O(n)。如果插入也很頻繁,換成 sortedcontainers.SortedList,插入和查詢都能到 O(log n)。


正在準備 TikTok 的 OA? 我們熟悉 CodeSignal General Coding Assessment 的題庫與節奏,能提供全程 OA輔助 / OA代面 服務,陪你把高頻題型和邊界處理一次打磨到位。

立即添加微信 Coding0201取得一對一客製備考方案

聯繫方式