Google SDE VO Coding real test review: full analysis of three-layer follow-up

1,377 Views

Google biggest difference between SDE VO Coding and other programs is that follow-up will continue to follow. A question can change from simple to complex. The test is whether you can adjust your thinking in real time as the question changes. This article is a complete review of the two questions I encountered, including the problem-solving process of each level of follow-up.

Google SDE VO Coding real test review: full analysis of three-layer follow-up

Topic 1: Log processing

Basic questions

Process a batch of logs. Each log contains timestamp and message content. The requirements are:

  • Deduplication (use message content as unique identifier)
  • Sort by timestamp in ascending order
  • Output results

Problem-solving ideas:

Use a hash table to record the content of messages that have appeared, and sort them by timestamp after deduplication.

Def process_logs(logs):
    # logs: [(timestamp, message), ...]
    seen = {}
    for timestamp, message in logs:
        if message not in seen:
            seen[message] = timestamp

    # Sort by timestamp in ascending order
    result = sorted(seen.items(), key=lambda x: x[1])
    return [(ts, msg) for msg, ts in result]

Pitfalls:

This question is most easily misled by "original order". After deduplication, the data must be sorted by timestamp, not output in the order of input. During the interview, first confirm the ranking basis with the interviewer.

Follow-up 1: Keep the one with the largest timestamp

After deduplication, the same message content appears multiple times, and the one with the largest timestamp is retained.

Def process_logs_keep_latest(logs):
    seen = {}
    for timestamp, message in logs:
        if message not in seen or seen[message] < timestamp:
            seen[message] = timestamp

    result = sorted(seen.items(), key=lambda x: x[1])
    return [(ts, msg) for msg, ts in result]

The change is very small. You only need to add a conditional judgment when updating the hash table and keep the one with a larger timestamp.

Follow-up 2: Real-time data flow, collecting and outputting ordered results at the same time

Data is no longer provided all at once, but flows in in real time, requiring ordered results to be output while receiving.

Idea:

Use the min heap to maintain the current ordered results. Every time new data comes in and is deduplicated, it is inserted into the heap. The top of the heap is always the one with the smallest timestamp.

Import heapq

class LogProcessor:
    def __init__(self):
        self.seen = {}
        self.heap = [] # (timestamp, message)

    def add_log(self, timestamp: int, message: str):
        if message in self.seen:
            return # Remove duplicates and ignore them directly
        self.seen[message] = timestamp
        heapq.heappush(self.heap, (timestamp, message))

    def get_next(self):
        if self.heap:
            return heapq.heappop(self.heap)
        return None

Follow-up question: If the de-redefinition is to keep the latest one, how to change it in streaming?

The old data in the heap needs to be lazily deleted - when new data is inserted, the old data is marked as invalid, and when it is popped out, it is judged whether it is valid.

Question 2: How many lines does the text occupy?

First level: no line breaks

Given a string and the width of the text box, calculate how many lines the text occupies. There are no newlines in the string.

Simply divide the total length by width and round up:

Import math

def count_lines(text: str, width: int) -> int:
    if not text:
        return 0
    return math.ceil(len(text) / width)

For example, length 10, width 3, the result is 4 lines.

Second level: join \n

There is in the string \n, wrap directly when encountering a newline character.

模拟显示过程,维护当前行数和当前行已放的字符数:

Def count_lines_with_newline(text: str, width: int) -> int:
    lines=1
    current = 0

    for ch in text:
        if ch == '\n':
            lines += 1
            current = 0
        else:
            current += 1
            if current > width:
                lines += 1
                current = 1 # put the current character on a new line

    return lines

Pitfalls:

After the line break, current returns to zero, but if the line break exceeds the width and the current character needs to be placed on a new line, current should start from 1, not 0.

The third level: two-column table

Upgrade to a two-column table. Each row has two left and right cells. The total width is fixed. You need to find the left and right width allocation that minimizes the height of the entire table.

Idea:

Enumerate left column width, right column width = total width – left column width. For each type of allocation, calculate the height of the left and right paragraphs of text respectively, take the maximum left and right height of each line, and find the allocation plan with the smallest total height.

Def min_table_height(left_text: str, right_text: str, total_width: int) -> int:
    best = float('inf')
    best_left_width = 1

    for left_width in range(1, total_width):
        right_width = total_width - left_width

        left_height = count_lines_with_newline(left_text, left_width)
        right_height = count_lines_with_newline(right_text, right_width)

        #The table height takes the maximum value of the two columns
        table_height = max(left_height, right_height)

        if table_height < best:
            best = table_height
            best_left_width = left_width

    return best

Follow-up question: What if there are multiple rows? Each row has left and right cells?

Def min_table_height_multi_row(rows: list, total_width: int) -> int:
    # rows: [(left_text, right_text), ...]
    best = float('inf')

    for left_width in range(1, total_width):
        right_width = total_width - left_width
        total_height = 0

        for left_text, right_text in rows:
            left_h = count_lines_with_newline(left_text, left_width)
            right_h = count_lines_with_newline(right_text, right_width)
            total_height += max(left_h, right_h)

        if total_height < best:
            best = total_height

    return best

Google VO coding has several things in common:

Each question will start with a simple version and then continue to add restrictions. Don’t rush to optimize after completing the basic version. Wait for the interviewer to give you a follow-up before deciding whether to change the structure.

It is best to reuse the previous functions for each layer of follow-up. The interviewer is looking at your code organization ability. The third-level table question directly called the second-level count_lines function, and he was very satisfied. When encountering "sorting" or "order" type questions, first ask what the sorting basis is, and don't make assumptions on your own.

There were two classmates around me who applied to Google at the same time. One prepared it himself and the other searched for it. ProgramHelp Make VO assists. Prepare yourself for that second round stuck in follow-up and fail. After finding an assist, the whole process went smoothly, and now he has joined the job. It’s not that you can’t prepare yourself, but Google’s follow-up direction is too difficult to predict. With North American CS experts on hand to give you ideas in real time, the error tolerance rate is much higher.

author avatar
Jory Wang Amazon Senior Software Development Engineer
Amazon senior engineer, focusing on the research and development of infrastructure core systems, with rich practical experience in system scalability, reliability and cost optimization. Currently focusing on FAANG SDE interview coaching, helping 30+ candidates successfully obtain L5/L6 Offers within one year.
END
 0