Infection Spread on a Grid (Cellular Automaton)
Company: Google
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: hard
Interview Round: Technical Screen
## Infection Spread on a Grid (Cellular Automaton)
You are simulating how an infection spreads across a 2D grid of cells over a sequence of discrete time steps. The grid is a rectangular matrix with `R` rows and `C` columns. Each cell is in exactly one of these states:
- `H` — healthy (susceptible)
- `I` — infected
- `R` — recovered/removed (immune; can never be infected again)
- `W` — wall/empty (a permanent barrier; never changes state and does not transmit infection)
Time advances in **discrete steps**. At each step, every cell is updated **simultaneously** based on the configuration at the **start** of that step (a classic synchronous cellular-automaton update — a cell that becomes infected during a step does not infect its neighbors until the *next* step).
Two cells are **neighbors** if they are orthogonally adjacent (up, down, left, right). Diagonal cells are **not** neighbors.
Implement the simulation in the parts below. Each part builds on the previous one.
---
### Part 1 — One step of basic spread
Given the grid, compute the grid after exactly **one** step under this rule:
- A `H` (healthy) cell becomes `I` (infected) in the next step if **at least one** of its orthogonal neighbors is `I` at the start of the step.
- `I`, `R`, and `W` cells keep their state.
Updates are simultaneous. Return the resulting grid.
**Example**
```
Start: After 1 step:
H H H H I H
H I H ---> I I I
H H H H I H
```
---
### Part 2 — Simulate `k` steps
Generalize Part 1 to run the simulation for `k` steps (`k >= 0`) and return the grid after the `k`-th step. With `k = 0` the grid is returned unchanged. Walls (`W`) continue to block spread (infection cannot pass through or into a wall), and recovered (`R`) cells remain immune.
**Example**
```
Start: k = 2
H H H H H
H I W H H
H H W H H
H H H H H
After 2 steps:
I I I H H
I I W H H
I I W H H
H I H H H
```
(The wall column blocks the infection from crossing directly through it; the infection must travel around.)
---
### Part 3 — Infection with a recovery delay
Now infected cells recover. Each cell carries a **time-since-infection** counter. The rules per step (applied simultaneously) are:
- A `H` cell with at least one `I` neighbor becomes `I` next step, with its infection counter set to `1`.
- An `I` cell that has been infected for fewer than `d` steps stays `I` and increments its counter.
- An `I` cell that has been infected for exactly `d` steps becomes `R` (recovered) next step. Recovered cells are immune forever and never become infected again.
- `R` and `W` cells never change.
Here `d >= 1` is the **infectious duration**. (When `d` is effectively infinite, this reduces to Part 2.)
Run the simulation for `k` steps and return the final grid (each cell as `H`, `I`, `R`, or `W`).
**Example**
```
d = 1, k = 2
Start: After 1 step: After 2 steps:
H H H H I H I R I
H I H ---> I R I ---> R R R
H H H H I H I R I
```
(With `d = 1`, a cell infected at one step recovers at the very next step, but it still infects its healthy neighbors during the step in which it is infected.)
---
### Part 4 — Stop early when the grid stabilizes
Combine all earlier rules (spread with infectious duration `d`, walls, permanent immunity). The simulation may be requested to run up to `k` steps, but you must **stop early** as soon as the grid reaches a state in which **no cell can change in any subsequent step** — i.e., a fixed point: there is no `H` cell adjacent to an `I` cell, and there is no `I` cell remaining (every infection has either spread out or recovered).
Return a pair: the final grid **and** the number of steps actually simulated. If the grid is already stable at the start, return it with `0` steps. If it never stabilizes within `k` steps, return the grid after `k` steps along with `k`.
Be precise about the stopping condition: the grid is stable only when **no further transition is possible** — both "no new infections can occur" **and** "no infected cell is still mid-recovery." A grid that still contains an `I` cell which will recover next step is **not** yet stable, even if no healthy cell is adjacent to an infection.
**Example**
```
d = 2, k = 100
Start:
H H H
H I H
H H H
The infection spreads outward, every infected cell eventually recovers, and once
the whole reachable region is R (and no I remains) no further change is possible.
Return that final all-recovered grid together with the exact number of steps taken
to reach it (which is far fewer than 100).
```
---
### Input / Output
- **Input**: the grid as a list of strings (or a 2D array of characters) over the alphabet `{H, I, R, W}`; integers `k` and (for Parts 3–4) `d`.
- **Output**:
- Parts 1–3: the resulting grid in the same format.
- Part 4: the resulting grid **and** the integer count of steps actually simulated.
### Constraints
- `1 <= R, C <= 1000`; `1 <= R * C <= 10^6`.
- `0 <= k <= 10^6`.
- `1 <= d <= 10^6`.
- The input grid contains only the characters `H`, `I`, `R`, `W`.
- Aim for an approach whose total work scales with the number of cells and the spread, rather than re-scanning the entire grid for every one of up to `k` steps when most of the grid is inert.
Quick Answer: This question tests a candidate's ability to implement and optimize a synchronous cellular automaton simulation on a 2D grid, assessing proficiency in BFS-based propagation, state machine design, and incremental computation. It falls under algorithms and grid simulation, evaluating practical coding skill and the ability to handle progressively complex constraints including recovery delays, barriers, and early-termination fixed-point detection.
Infection Spread on a Grid — Part 1: One Step of Basic Spread
You simulate an infection on a 2D grid. Each cell is one of:
- `H` — healthy (susceptible)
- `I` — infected
- `R` — recovered/removed (immune forever)
- `W` — wall/empty (permanent barrier; never changes, never transmits)
Compute the grid after exactly **one** synchronous step. All updates happen simultaneously based on the configuration at the **start** of the step:
- A `H` cell becomes `I` if **at least one** of its four orthogonal neighbors (up/down/left/right — not diagonal) is `I` at the start of the step.
- `I`, `R`, and `W` cells keep their state.
Return the resulting grid in the same list-of-strings format.
**Example**
```
Start: After 1 step:
H H H H I H
H I H ---> I I I
H H H H I H
```
Constraints
- 1 <= R, C and 1 <= R*C <= 10^6
- Grid contains only the characters H, I, R, W
- Updates are simultaneous (read the start-of-step state, not partially-updated cells)
- Only orthogonal neighbors count; diagonals do not transmit
Examples
Input: (['HHH', 'HIH', 'HHH'],)
Expected Output: ['HIH', 'III', 'HIH']
Explanation: The single central infection spreads to its four orthogonal neighbors simultaneously; corners stay H (only diagonally adjacent to the I).
Input: (['HHH'],)
Expected Output: ['HHH']
Explanation: No I anywhere, so nothing changes.
Input: (['I'],)
Expected Output: ['I']
Explanation: Single infected cell; an I keeps its state.
Input: (['HWH', 'WIW', 'HWH'],)
Expected Output: ['HWH', 'WIW', 'HWH']
Explanation: The I at the center is walled in on all four orthogonal sides, so no H is adjacent to an I; corners are only diagonal. Nothing changes.
Input: (['HIRH', 'WHHI'],)
Expected Output: ['IIRI', 'WIII']
Explanation: Edge case mixing all four states. (0,0)->I from (0,1); (0,3)->I from (1,3); (1,1)->I from (0,1); (1,2)->I from (1,3). R and W are untouched.
Hints
- Write results into a separate copy of the grid so a cell freshly turned I in this step does not infect its neighbors until the next step.
- Only H cells can change; for each H, scan its 4 orthogonal neighbors for an I.
- W cells neither change nor transmit — treat them like any non-I neighbor (they simply aren't I).
Infection Spread on a Grid — Part 2: Simulate k Steps
Same synchronous spread rule as Part 1 (a `H` cell becomes `I` if at least one orthogonal neighbor is `I`; `I`/`R`/`W` keep their state), but now run the simulation for `k` steps (`k >= 0`) and return the grid after the `k`-th step.
- With `k = 0` the grid is returned unchanged.
- Walls (`W`) keep blocking spread — infection cannot pass through or into a wall, so it must travel around.
- Recovered (`R`) cells stay immune.
**Example** (`k = 2`)
```
Start: After 2 steps:
H H H H H I I I H H
H I W H H --> I I W H H
H H W H H I I W H H
H H H H H H I H H H
```
The wall column blocks the infection from crossing directly through it; the infection must travel around.
Constraints
- 1 <= R, C and 1 <= R*C <= 10^6
- 0 <= k <= 10^6
- Grid contains only H, I, R, W
- Updates simultaneous each step; walls block, recovered stay immune
Examples
Input: (['HHHHH', 'HIWHH', 'HHWHH', 'HHHHH'], 2)
Expected Output: ['IIIHH', 'IIWHH', 'IIWHH', 'HIHHH']
Explanation: Two steps of outward spread; the wall column (W) forces the infection to route around it rather than crossing directly.
Input: (['HHH', 'HIH', 'HHH'], 0)
Expected Output: ['HHH', 'HIH', 'HHH']
Explanation: k = 0 returns the grid unchanged.
Input: (['HHH', 'HIH', 'HHH'], 1)
Expected Output: ['HIH', 'III', 'HIH']
Explanation: One step reproduces the Part 1 result.
Input: (['HHHHH'], 100)
Expected Output: ['HHHHH']
Explanation: No infection seed, so even 100 steps change nothing (early stop on the first step).
Input: (['HHHHH', 'HHIHH', 'HHHHH'], 10)
Expected Output: ['IIIII', 'IIIII', 'IIIII']
Explanation: The whole open grid is reachable within a few steps; after it fully saturates, the early-stop prevents wasted iterations.
Input: (['I'], 5)
Expected Output: ['I']
Explanation: Single I with no H to infect; stays I for any k.
Hints
- Apply the Part 1 single-step transform k times, each time into a fresh copy.
- If a full step changes nothing, the grid is frozen — break early instead of looping up to k=10^6 times.
- A multi-source BFS from all initial I cells (skipping W) gives each H its infection time; H is infected after step k iff its distance to the nearest initial I is <= k. This avoids re-scanning the whole grid every step.
Infection Spread on a Grid — Part 3: Recovery After Infectious Duration d
Extend Part 2 so infected cells eventually recover. Each cell tracks a **time-since-infection** counter. Per synchronous step (all simultaneous):
- A `H` cell with at least one `I` neighbor becomes `I` next step, counter = 1.
- An `I` cell infected for **fewer than `d`** steps stays `I` and increments its counter.
- An `I` cell infected for **exactly `d`** steps becomes `R` next step. Recovered cells are immune forever.
- `R` and `W` cells never change.
Here `d >= 1` is the infectious duration. Treat each `I` present in the input grid as freshly infected (counter starts at 1). A cell that recovers this step still infected its healthy neighbors during the step in which it was `I` (neighbors are decided from the start-of-step state).
Run `k` steps and return the final grid.
**Example** (`d = 1, k = 2`)
```
Start: After 1 step: After 2 steps:
H H H H I H I R I
H I H ---> I R I ---> R R R
H H H H I H I R I
```
Constraints
- 1 <= R, C and 1 <= R*C <= 10^6
- 0 <= k <= 10^6 and 1 <= d <= 10^6
- Grid contains only H, I, R, W
- Updates simultaneous; recovery triggers once an I's counter reaches d; an I still infects during its final infectious step
Examples
Input: (['HHH', 'HIH', 'HHH'], 1, 1)
Expected Output: ['HIH', 'IRI', 'HIH']
Explanation: With d=1 the center (counter 1 = d) recovers to R after one step, but it infected its 4 neighbors during that same step.
Input: (['HHH', 'HIH', 'HHH'], 2, 1)
Expected Output: ['IRI', 'RRR', 'IRI']
Explanation: Step 2: the 4 edge I's (each at counter 1 = d) recover to R while infecting the corners, leaving a ring of new I around an all-R core.
Input: (['HHH', 'HIH', 'HHH'], 0, 3)
Expected Output: ['HHH', 'HIH', 'HHH']
Explanation: k = 0 returns the grid unchanged regardless of d.
Input: (['HHHHH', 'HHIHH', 'HHHHH'], 1, 2)
Expected Output: ['HHIHH', 'HIIIH', 'HHIHH']
Explanation: d=2 so the seed (counter 1 < 2) stays I and just spreads outward this step; no recovery yet.
Input: (['HHHHH', 'HHIHH', 'HHHHH'], 2, 2)
Expected Output: ['HIIIH', 'IIRII', 'HIIIH']
Explanation: Step 2: the seed reaches counter 2 = d and recovers to R; the step-1 infections (counter now 1<2) stay I and spread one ring further.
Input: (['I'], 1, 1)
Expected Output: ['R']
Explanation: A lone I with counter 1 = d recovers to R in one step.
Input: (['IWI', 'WHW', 'IWI'], 3, 5)
Expected Output: ['IWI', 'WHW', 'IWI']
Explanation: The central H is walled off on all orthogonal sides and the corner I's (counter < d=5) never recover within k=3 steps, so the grid is frozen.
Hints
- Carry a separate integer counter per cell alongside the state; initialize every input I to counter 1.
- Compute next state and next counter from the start-of-step snapshot only — never read a value you already updated this step.
- Order matters within a cell: infection of neighbors is decided from the OLD state, so a cell that recovers to R this step still counts as I when its neighbors look at it.
- When d is huge (effectively infinite) nothing ever recovers and this reduces to Part 2.
Infection Spread on a Grid — Part 4: Stop Early at a Fixed Point
Combine all earlier rules (spread with infectious duration `d`, walls, permanent immunity). The simulation may be requested to run up to `k` steps, but you must **stop early** as soon as the grid reaches a fixed point where no cell can change in any subsequent step.
The grid is stable only when **no further transition is possible** — both: there is no `H` cell adjacent to an `I`, and there is no `I` cell remaining (every infection has either spread out or recovered). Equivalently: the grid is stable iff it contains no `I` cell at all. A grid that still has an `I` which will recover next step is **not** stable, even if no healthy cell is adjacent to an infection.
Return a pair: the final grid **and** the number of steps actually simulated.
- If the grid is already stable at the start, return it with `0` steps.
- If it never stabilizes within `k` steps, return the grid after `k` steps along with `k`.
In Python return a tuple `(grid, steps)`. In Java/C++/JS return the pair as `[grid, steps]`.
**Example** (`d = 2, k = 100`, single central seed in a 3x3 grid)
The infection spreads outward, every infected cell eventually recovers, and once the whole reachable region is `R` with no `I` left, no further change is possible. The answer is the all-`R` grid together with the exact step count to reach it — here **4** steps, far fewer than 100.
Constraints
- 1 <= R, C and 1 <= R*C <= 10^6
- 0 <= k <= 10^6 and 1 <= d <= 10^6
- Grid contains only H, I, R, W
- Stable iff no I cell remains (then no H can be infected and no I is mid-recovery)
- Already-stable start returns 0 steps; never stabilizing within k returns k
Examples
Input: (['HHH', 'HIH', 'HHH'], 100, 2)
Expected Output: (['RRR', 'RRR', 'RRR'], 4)
Explanation: The seed spreads to fill the 3x3, every cell recovers, and the grid becomes all-R with no I after exactly 4 steps — so it stops at 4 rather than running to k=100.
Input: (['HHH', 'HHH', 'HHH'], 100, 2)
Expected Output: (['HHH', 'HHH', 'HHH'], 0)
Explanation: No I anywhere, so the grid is already stable: return it with 0 steps.
Input: (['HHH', 'HIH', 'HHH'], 1, 1)
Expected Output: (['HIH', 'IRI', 'HIH'], 1)
Explanation: After one step the grid still contains I cells, but k=1 caps the run; return the post-step grid with step count 1.
Input: (['I'], 100, 1)
Expected Output: (['R'], 1)
Explanation: The lone I recovers in step 1, after which no I remains; stops at 1 step.
Input: (['HHH', 'HIH', 'HHH'], 2, 100)
Expected Output: (['III', 'III', 'III'], 2)
Explanation: With d=100 nothing recovers; the grid still has I cells after k=2, so it never stabilizes within k and returns the grid after 2 steps with count 2.
Input: (['RRR', 'RWR', 'RRR'], 100, 3)
Expected Output: (['RRR', 'RWR', 'RRR'], 0)
Explanation: Only R and W cells, no I — already a fixed point, returned with 0 steps.
Hints
- Check the stopping condition at the START of each step: if the grid has no I, it is a fixed point — return the current grid and the count of steps taken so far.
- 'No I remaining' is the whole condition: if any I exists it either has an H neighbor (will spread) or will recover next step — both are changes.
- Increment your step counter only when you actually perform a transition; an already-stable input must report 0.
- Track the active frontier (cells that are I or H-adjacent-to-I) instead of rescanning the full grid each step, so a mostly-inert grid stabilizes cheaply.