Guides Guide 3 Process

The Interview Script

The same seven phases, every time, in the same order. A script frees your attention for the actual problem, and it keeps you moving when the problem is hard.

Two candidates can write the same final code and get opposite verdicts. The difference is almost always the first ten minutes and the last five. This page is what to do in those minutes, with the C# details that come up along the way.

Contents

  1. The time budget
  2. 1. Clarify
  3. 2. Work an example by hand
  4. 3. State the brute force
  5. 4. Optimise, out loud
  6. 5. Code
  7. 6. Test
  8. 7. Complexity and follow-ups
  9. When you are stuck
  10. When the interviewer hints
  11. A worked transcript

The time budget

For a 45-minute slot with one problem. Shrink the middle if there are two.

Do not start coding before minute 10. The most common failure is not a wrong algorithm. It is coding the right algorithm for the wrong problem. Ten minutes of alignment is cheap. Rewriting at minute 35 is not.

1. Clarify

Ask until you could write the method signature with no guesswork. Seven questions cover most problems in C#.

Close the phase by restating. “So: given an int array that may contain negatives, count the contiguous subarrays summing to exactly k, with n up to a hundred thousand. Is that right?” If they say yes, you are aligned. If they correct you, you just saved twenty minutes.

2. Work an example by hand

Take the given example and compute the answer yourself, on the board, slowly. It forces you to understand the rule. It also often shows you the pattern before you go looking for it.

Do not invent five edge cases here. One is enough to show the habit. Five eats your coding time and reads as stalling. Save the rest for the testing phase, where they belong.

3. State the brute force

Say it. Cost it. Do not write it.

The template sentence: “The brute force is to check every pair of indices, compute the sum of each range, and count the matches. That is O(n²) time and O(1) space. With n at a hundred thousand that is 10¹⁰ operations, so it will not pass. Let me find something better.”

This costs thirty seconds and does three things. It proves you understand the problem. It gives you a correctness baseline for later. And it makes the optimisation a deliberate step rather than a lucky guess. Some interviewers will let you keep it as a fallback if time runs out, which is far better than nothing.

The one exception. If the brute force is the expected answer, given the constraints, say so and code it. “n is at most 20, so 2ⁿ is about a million and exhaustive search is intended.” Optimising past the requirement wastes time and can add bugs.

4. Optimise, out loud

This is the phase graded hardest. Make your reasoning audible. Five moves, in order:

  1. Name the waste. “The brute force recomputes the same range sums over and over.”
  2. Name the pattern. “Contiguous plus an exact target plus negatives, so a window will not work. This is prefix sums with a dictionary.”
  3. Justify it. “A range sum is a difference of two prefixes, so I can look up the partner instead of scanning for it.”
  4. Cost it before coding. “That is one pass, O(n) time and O(n) space. That fits the constraint.”
  5. Get agreement. “Shall I code that?”
Always ask before coding. It takes two seconds and gives the interviewer a natural moment to redirect you. They usually want to. A candidate who checks in gets steered. A candidate who charges ahead gets watched while failing.

5. Code

Write it the way you would want to read it in a review.

Do

Do not

Talking and coding at once is hard, and it is a skill you can learn. Practise it on purpose: solve problems out loud, alone, on a timer. It feels silly. It is the single highest-return thing you can rehearse.

6. Test

Do not say “I think that works”. Prove it, out loud, on the example still on the board. The method is in Guide 4. The short version:

  1. Dry-run the main example, tracking every variable in a small table.
  2. Run the edge case you invented in phase 2.
  3. Check the boundaries: first pass, last pass, and the exit condition.
  4. If you find a bug, say what it is before you fix it. Diagnosing out loud scores. Silently editing does not.
Finding your own bug is a good signal, not a bad one. The interviewer is deciding whether they would trust your code without checking it. A candidate who catches their own off-by-one is more reassuring than one whose code happened to be right.

7. Complexity and follow-ups

State time and space using the four-part form in Guide 2. Then offer one thing you would do next. Good closers:

One is plenty. Offering an improvement you had no time to build still shows you saw it.

When you are stuck

Being stuck is normal and is not a failure. Being stuck and silent is. Work down this list, out loud.

Do not go silent, and do not keep apologising. “Sorry, I am so slow” costs you twice. It wastes time, and it invites the interviewer to agree. Replace it with a status update. “Here is what I have and here is what I am missing” is the same information, framed as progress.

When the interviewer hints

A hint is not a penalty. Interviewers hint because they want you to finish. How you take it is what gets scored.

A worked transcript

Problem: count the subarrays summing to exactly k. Compressed, but this is the shape.

Clarify. “How big can the array get? … A hundred thousand, so I need about O(n log n) or better. Can the values be negative? … Yes, good, that matters. Can the array be empty? … Yes, and then the answer is zero. Can the count pass the int range? … No, so I can return an int. And I want the count of subarrays, not one example?”
Example. “Let me take [1, 2, 3] with k = 3. The subarrays are [1], [1,2], [1,2,3], [2], [2,3], [3]. Two of them sum to 3, so the answer is 2. And an edge case: [0, 0] with k = 0 should be 3, because both singles and the pair all work.”
Brute force. “Every start, every end, sum each range. O(n²) with a running total, O(n³) if I re-sum naively. At a hundred thousand that is 10¹⁰, too slow. Let me improve it.”
Optimise. “A window is out, because values can be negative and the target is exact, so shrinking is not safe. But a range sum is a difference of two prefix sums. I want P[j] - P[i] = k, which rearranges to P[i] = P[j] - k. So as I walk along keeping a running prefix, I ask a dictionary how many earlier prefixes equal running - k. That is the Two Sum rearrangement. One pass, O(n) time and O(n) space. Shall I code it?”
Code. “I will seed the dictionary with zero mapped to one, standing for the empty prefix. Otherwise I lose every subarray that starts at index 0. I look up before recording the current prefix, so a prefix cannot pair with itself. The running sum is a long, because a hundred thousand values near a billion would overflow an int.”
public static class SubarraySum
{
    /// <summary>Counts contiguous subarrays whose sum is exactly k. O(n) time and space.</summary>
    /// <param name="nums">The values. Negatives and zeros are allowed.</param>
    /// <param name="k">The target sum.</param>
    /// <returns>The number of subarrays that sum to k.</returns>
    /// <example>SubarraySum.Count([1, 2, 3], 3) returns 2.</example>
    public static int Count(int[] nums, int k)
    {
        // prefix sum -> how many times it has appeared so far.
        // Seed: the empty prefix has sum 0 and appears 1 time, so ranges from index 0 count.
        var seen = new Dictionary<long, int> { [0] = 1 };
        long running = 0;   // 0 is the sum of the empty prefix. long, so it cannot overflow.
        int count = 0;      // 0 matches found so far

        // Each pass ends a range at this value.
        // Invariant: seen holds every prefix that ends BEFORE this value.
        foreach (int x in nums)
        {
            running += x;
            // Look up first, so the current prefix cannot pair with itself.
            count += seen.GetValueOrDefault(running - k);   // a miss reads as 0 matches
            seen[running] = seen.GetValueOrDefault(running) + 1;   // + 1 records this prefix
        }
        return count;
    }
}
Test. “On [1, 2, 3] with k = 3: running goes 1, 3, 6. At running = 3 I look for 0, which is in the dictionary once, so count is 1. At running = 6 I look for 3, which is there once, so count is 2. Correct. On [0, 0] with k = 0: running stays 0. The first step looks up 0 and finds the seed, so count is 1. Then it records, so 0 maps to 2. The second step looks up 0 and finds two, so count is 3. Correct.”
Close. “O(n) time, O(n) space for the dictionary. n is the array length. The space is worst case when every prefix is distinct. If the question changed to the longest such subarray, I would store the earliest index per prefix instead of a count. I would use TryAdd, so an existing key is never overwritten.”

The five things to carry forward


← Guide 2 — Constraints and Complexity Guide 4 — Testing Your Own Code →