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.
For a 45-minute slot with one problem. Shrink the middle if there are two.
1. Clarify, 3 minutes. You can restate the problem in one sentence.
2. Example by hand, 2 minutes. One worked case on the board, plus one edge case.
3. Brute force, 2 minutes. Named and costed. Not coded.
4. Optimise, 6 minutes. A pattern named, an approach agreed.
5. Code, 18 minutes. Working code, narrated as you go.
6. Test, 6 minutes. A dry run and the edge cases.
7. Complexity and follow-ups, 3 minutes. Time, space, and one improvement you would make.
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#.
“How large can the input get?” It picks the target complexity. See Guide 2.
“Can the values be negative? Zero?” Kills or enables the sliding window outright.
“Can the input be empty, or null?” Decides your guard clauses. In C#, null and empty are two different cases.
“Are duplicates possible?” Changes the dedup logic in 3Sum, backtracking, and rotated binary search.
“Am I allowed to modify the input?” Decides whether Array.Sort(nums) is fine, and chooses between cyclic sort and Floyd.
“If several answers are valid, does it matter which?” Decides whether you need a tie-break, as in topological sort.
“Can the result pass the int range?” Decides between int and long for the return type and the running totals.
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.
Use their example first, so you and they are looking at the same thing.
Then invent one awkward case: empty, single element, all duplicates, or all negative. Ask what the expected output is.
Write both down and leave them on the board. You will reuse them in phase 6.
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:
Name the waste. “The brute force recomputes the same range sums over and over.”
Name the pattern. “Contiguous plus an exact target plus negatives, so a window will not work. This is prefix sums with a dictionary.”
Justify it. “A range sum is a difference of two prefixes, so I can look up the partner instead of scanning for it.”
Cost it before coding. “That is one pass, O(n) time and O(n) space. That fits the constraint.”
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
Start with the signature and types.public static int CountSubarrays(int[] nums, int k) confirms the contract one last time.
Guard clauses first. Null or empty input, a single element. It gets the edges out of the way.
Name variables for what they mean.windowSum, not s. firstIndex, not d.
Write the invariant as a comment. One line. It is how you and the reviewer both stay convinced.
Narrate the non-obvious lines only. “I look up before recording, so a prefix cannot pair with itself.”
Use a local function when a block gets long. Small named pieces read better under time pressure, not worse.
Do not
Narrate every line. “Now I set i to zero” is noise. Explain decisions, not syntax.
Go silent for three minutes. The interviewer cannot score thinking they cannot hear.
Golf it into one LINQ chain. A dense chain is harder to debug, harder to grade, and hides its cost.
Fight the compiler on nullable warnings. In an interview, say “I would handle null here” and move on.
Silently change approach midway. Say “I have realised this misses X, so I am switching to Y”.
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:
Dry-run the main example, tracking every variable in a small table.
Run the edge case you invented in phase 2.
Check the boundaries: first pass, last pass, and the exit condition.
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:
“I could drop the space to O(1) with two pointers instead of the two arrays.”
“If this ran repeatedly over the same data, I would build the prefix array once.”
“The sort dominates. If the values were bounded I could bucket them and get linear time.”
“In production I would count with CollectionsMarshal.GetValueRefOrAddDefault, which hashes once instead of twice.”
“The recursion is as deep as the input. For large inputs I would switch to an explicit Stack<T>.”
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.
Shrink it. Solve n = 1, then n = 2, then n = 3 by hand. The recurrence often appears.
Re-read the constraints. “n is at most 20” is an instruction. See the hint list.
Walk the pattern triggers. Sorted? Contiguous? All paths? Fewest steps? Run down the 23 triggers.
Change the data structure. What if this were sorted? In a Dictionary? In a PriorityQueue? In a SortedSet? On a Stack?
Invert the question. “Fewest to remove” is “most to keep”. “Rooms needed” is “peak overlap”.
Solve a relaxed version. Drop a constraint, solve that, then add the constraint back.
Say where you are. “I have the O(n²) version. I am looking for a way to avoid rescanning the left side. Can I think for a minute?”
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.
“What if the array were sorted?”
They mean: sort it.
Take it at once. “Good idea, then two pointers work because…”
“Do you need to recompute that?”
They mean: you have a redundant scan.
Find the repeated work. It is usually a cache or a running total.
“Walk me through your example again.”
They mean: there is a bug and they want you to find it.
Dry-run slowly. Do not defend the code.
“Is that the best you can do?”
They mean: no.
Say your current complexity, then name the next target.
“What happens if the input is empty?”
They mean: your code crashes, perhaps on nums[0] or Max().
Trace it honestly, then add the guard.
“What if the numbers are very large?”
They mean: your int overflows.
Switch the running total to long, and say where the overflow would happen.
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
Do not code before minute 10. Clarify, example, brute force, agree an approach.
Ask the clarifying questions, including int or long, then restate the problem and get a yes.
State the brute force and its cost. Never write it, unless it is the intended answer.
Ask before coding. It gives the interviewer a free chance to redirect you.
Stuck is fine, silent is not. Give a status update instead of an apology.