← TikTok Interview Insights

TikTok·Software Engineer·Onsite - Coding / Algorithms·Intermediate

Intermediate
Jun 2026

Summary

Four-question algorithmic gauntlet for a TikTok SWE role. The problems ranged from classic DP to some greedy reasoning and a sneaky simulation at the end. Felt like a lot to cover in one sitting.

Questions Asked (4)

Q1

Implement a wildcard pattern matcher where '?' matches any single character and '*' matches any sequence including empty. Discuss DP versus greedy approaches and analyze the complexity of each.

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

I went straight to 2D DP which felt safe, but they pushed back asking if I could do better on space.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the problem and edge cases, then present both DP and greedy solutions with their complexities. Emphasize the DP approach for its clarity and the greedy approach for its optimal space, and discuss trade-offs in terms of time, space, and implementation complexity.

Pro tip: Mention that the greedy approach with backtracking is often preferred in practice for its O(1) space, but be prepared to code the DP solution if asked for a more straightforward implementation. Also, highlight that the DP solution can be optimized to O(n) space using rolling arrays.

1. Clarify requirements and edge cases

Ask clarifying questions about the input constraints, character sets, and expected behavior for edge cases like empty strings or patterns with consecutive asterisks.

2. Present the DP approach

Explain the dynamic programming solution with a 2D table where dp[i][j] indicates if the first i characters of the string match the first j characters of the pattern. Derive the recurrence relations and state the time and space complexity.

3. Present the greedy approach

Describe the greedy algorithm that uses two pointers and backtracking for asterisks, achieving O(n) time and O(1) space. Explain how it handles multiple asterisks and why it works.

4. Compare and contrast

Discuss the trade-offs: DP is easier to reason about and implement but uses O(mn) space (or O(n) with optimization), while greedy is more space-efficient but trickier to get right. Mention that both have O(mn) worst-case time.

5. Discuss optimizations and extensions

Mention potential optimizations like reducing DP space to O(n) using rolling arrays, or handling multiple consecutive asterisks by collapsing them. Also, briefly touch on extensions like regular expression matching.

Key Points to Mention

  • DP recurrence: dp[i][j] = dp[i-1][j-1] if characters match or pattern has '?'; if pattern has '*', dp[i][j] = dp[i-1][j] || dp[i][j-1].
  • Time complexity: O(mn) for both DP and greedy (worst-case), where m is string length and n is pattern length.
  • Space complexity: DP uses O(mn) (or O(n) with optimization), greedy uses O(1).
  • Greedy backtracking: keep track of the last asterisk position and the corresponding string index to backtrack when mismatch occurs.
  • Edge cases: empty string, empty pattern, pattern with only '*', consecutive asterisks.
  • Practical considerations: greedy is often preferred for its space efficiency, but DP is more straightforward to implement and explain.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

Design a data structure initialized with two integer arrays A and B that supports an update operation adding a value to B[i], and a query operation counting pairs where a + b equals a given total. Handle up to 100,000 updates and queries efficiently.

Algorithms & Data StructuresSystem Design
Author's notes

This one tripped me up more than it should have.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Clarify the problem constraints and required operations, then propose a solution that maintains a frequency map of B values and uses a Fenwick tree (BIT) or segment tree over the value range to support point updates and range sum queries. For each query, iterate over distinct values in A and sum the frequencies of (total - a) in B, ensuring O((|A| + |B|) log V) or O(|A| log V) per query depending on the approach.

Pro tip: Mention that if the value range is large, you can compress coordinates or use a hash map combined with a balanced BST, but a Fenwick tree over compressed values is usually the most efficient and simplest to implement. Also, note that if A is static, precomputing its frequency map can reduce query time.

1. Clarify requirements and constraints

Ask about the range of values in A and B, the number of operations, and whether updates and queries are interleaved. Confirm that we need to count pairs (i, j) such that A[i] + B[j] = total after updates.

2. Choose data structures

Maintain a frequency map of B's current values. Use a Fenwick tree (BIT) or segment tree over the value range (or compressed values) to support point updates (increment/decrement frequency) and range sum queries (count of values in a range).

3. Design update operation

When adding a value v to B[i], decrement the frequency of the old B[i] in the BIT and increment the frequency of the new value. Update the frequency map accordingly.

4. Design query operation

For a given total, iterate over distinct values in A (or all elements if A is small) and for each a, query the BIT for the frequency of (total - a). Sum these frequencies to get the total number of pairs.

5. Analyze complexity and optimize

Update is O(log V), query is O(|A| log V) where |A| is the number of distinct values in A. If |A| is large, consider precomputing A's frequency map and iterating over the smaller of the two sets. Discuss trade-offs and potential optimizations.

Key Points to Mention

  • Use a Fenwick tree (BIT) or segment tree for efficient point updates and range sum queries.
  • Maintain a frequency map of B's values to handle duplicates and updates.
  • Coordinate compression if the value range is large (e.g., up to 10^9).
  • Time complexity: O(log V) per update, O(|A| log V) per query, where |A| is the number of distinct values in A.
  • Space complexity: O(V) or O(N) with compression, where N is the number of distinct values in B.
  • Consider edge cases: negative values, zero, large totals, and updates that change B[i] to a value already present.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

Implement an LRU cache supporting get and put in O(1) time. Describe your design choices.

Algorithms & Data Structures
Author's notes

Standard one.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the requirements (capacity, O(1) operations) and then describe a design using a hash map and a doubly linked list. Explain how the hash map provides O(1) access to nodes, while the linked list maintains the order of usage for eviction. Walk through the get and put operations, highlighting edge cases and complexity.

Pro tip: Mention that you would use a doubly linked list with sentinel head and tail nodes to simplify edge cases and avoid null checks, and discuss how this design can be extended to support thread safety if needed.

1. Clarify requirements and constraints

Confirm the expected operations (get, put), capacity, and that both must be O(1). Ask about concurrency requirements or other constraints.

2. Choose data structures

Select a hash map for O(1) key lookup and a doubly linked list to track usage order. Explain why a singly linked list or array would not work efficiently.

3. Detail the design

Describe how the hash map stores key -> node references, and the linked list maintains nodes in order of recent use (most recent at head). Include sentinel nodes to simplify operations.

4. Explain get and put operations

For get: if key exists, move node to head and return value; else return -1. For put: if key exists, update value and move to head; else create new node, add to head, and if capacity exceeded, remove tail node and delete from map.

5. Analyze complexity and edge cases

State that both operations are O(1) time and O(capacity) space. Discuss edge cases like capacity 0 or 1, updating existing keys, and handling null values.

Key Points to Mention

  • Hash map provides O(1) access to cache nodes.
  • Doubly linked list maintains usage order for O(1) eviction and updates.
  • Sentinel head and tail nodes simplify insertion and removal logic.
  • Both get and put operations are O(1) time complexity.
  • Space complexity is O(capacity) for storing up to capacity items.
  • Consider thread safety if the cache will be accessed concurrently.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q4

Given arrays of distances and speeds for n threats, starting at time 0 you may neutralize one threat per minute. A threat reaches the base when elapsed time is at least dist[i]/speed[i]. If any threat has already arrived before you act at a given minute, the process ends. Return the maximum number of threats you can neutralize.

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

This was the weird one.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Recognize that the optimal strategy is to neutralize threats in order of their arrival times (earliest deadline first). Sort threats by arrival time, then simulate minute by minute, neutralizing the next threat if it hasn't already arrived. Count how many you can neutralize before any threat arrives.

Pro tip: Clarify edge cases upfront, such as ties in arrival times and threats that arrive at time 0. Also, mention that if a threat arrives exactly at the minute you act, you can still neutralize it because the condition is 'at least' dist/speed.

1. Understand the problem

Restate the problem: you have n threats with given distances and speeds. Each minute you can neutralize one threat. A threat arrives when elapsed time >= dist/speed. If any threat has arrived before you act at a minute, the process ends. Maximize neutralized threats.

2. Identify optimal strategy

Argue that to maximize the number, you should prioritize threats that arrive earliest (earliest deadline first). This greedy approach is optimal because neutralizing a later threat first could cause an earlier threat to arrive and end the process.

3. Sort threats by arrival time

Compute arrival time for each threat as dist[i]/speed[i]. Sort threats in non-decreasing order of arrival time. If ties, any order works.

4. Simulate the process

Iterate minute by minute (or over sorted threats). At minute t (starting from 0), if the next threat's arrival time <= t, it has already arrived, so stop. Otherwise, neutralize it and increment count. Continue until no more threats or a threat has arrived.

5. Return the count

The number of threats neutralized before any arrival is the answer. Discuss time complexity: O(n log n) due to sorting, and space O(n) for storing arrival times.

Key Points to Mention

  • Greedy algorithm: earliest deadline first is optimal.
  • Sorting by arrival time (dist/speed) is key.
  • Simulation: check if threat has arrived before acting.
  • Edge cases: threats with arrival time 0, ties in arrival times, large n.
  • Time complexity: O(n log n) due to sorting; space O(n).
  • Proof of optimality: exchange argument or induction.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.