Zigzag Conversion
Problem
Given a string s and an integer numRows, return the string formed by writing s in a zigzag pattern on numRows and then reading line by line.
- 1 ≤ s.length ≤ 1000
- s consists of English letters (lower-case and upper-case), ',' and '.'
- 1 ≤ numRows ≤ 1000
Example
s = "PAYPALISHIRING", numRows = 3"PAHNAPLSIIGYIR"The zigzag pattern for numRows=3 is: P A H N A P L S I I G Y I R Reading line by line produces "PAHNAPLSIIGYIR". The algorithm simulates placing characters row by row, changing direction when reaching the top or bottom row. The critical moment is when the direction reverses at the boundaries, ensuring characters are assigned to the correct rows in zigzag order.
Approach
Straightforward Solution
A brute-force approach might try to build a 2D matrix representing the zigzag and then read it line by line. This wastes space and complicates indexing, especially for large strings and row counts.
Core Observation
The zigzag pattern can be viewed as placing characters sequentially in rows, moving downwards until the bottom row, then upwards until the top row, repeatedly. This movement creates a repeating cycle of row indices.
Path to Optimal
The key insight is to simulate the zigzag traversal by tracking the current row and direction (down or up). Instead of building a matrix, maintain an array of strings for each row and append characters accordingly. When the current row reaches either boundary (0 or numRows-1), reverse the direction. This approach uses O(n) time and O(n) space efficiently.
Optimal Approach
Initialize an array of empty strings for each row. Iterate over each character in s, appending it to the current row's string. Update the current row by moving in the current direction. Reverse direction when the top or bottom row is reached. Finally, concatenate all row strings to form the output.
Time
O(n)
Each character in the input string is processed exactly once, appending to one of the row strings, resulting in linear time complexity.
Space
O(n)
The auxiliary space is proportional to the input size, as all characters are stored distributed across the row strings before concatenation. This is necessary since the output string length equals the input length.
Pattern Spotlight
Simulation (Stateful Traversal)
When a problem requires constructing or interpreting a pattern with directional changes or cycles, simulate the process by tracking state variables (like current position and direction) instead of building explicit data structures, enabling efficient and clear solutions.
Solution
| 1 | class Solution: |
| 2 | def convert(self, s: str, numRows: int) -> str: |
| 3 | if numRows == 1: |
| 4 | return s |
| 5 | |
| 6 | rows = [""] * numRows |
| 7 | row = 0 |
| 8 | direction = 1 |
| 9 | |
| 10 | for ch in s: |
| 11 | rows[row] += ch |
| 12 | |
| 13 | if row == 0: |
| 14 | direction = 1 |
| 15 | elif row == numRows - 1: |
| 16 | direction = -1 |
| 17 | |
| 18 | row += direction |
| 19 | |
| 20 | return "".join(rows) |
Step-by-Step Solution
Handle Single Row Edge Case to Avoid Unnecessary Processing
| 3 | if numRows == 1: |
| 4 | return s |
Objective
To immediately return the input string if numRows is 1, since the zigzag pattern degenerates to a straight line.
Key Insight
When numRows is 1, the zigzag pattern is just the original string itself. This early exit prevents unnecessary computation and avoids edge case bugs related to direction changes and row indexing.
Interview Quick-Check
Core Logic
Recognizing that numRows=1 means no zigzag occurs and the input string is the output.
Common Pitfalls & Bugs
Failing to handle numRows=1 separately can cause index errors or incorrect direction changes.
Initialize Row Containers and Directional State for Zigzag Simulation
| 6 | rows = [""] * numRows |
| 7 | row = 0 |
| 8 | direction = 1 |
Objective
To prepare an array of strings for each row and initialize variables to track the current row and traversal direction.
Key Insight
Using an array of strings for rows allows efficient appending of characters without complex 2D structures. The current row and direction variables simulate the zigzag traversal by moving through rows and reversing direction at boundaries.
Interview Quick-Check
Core Logic
Storing characters in separate row strings simulates the zigzag pattern without extra space for empty cells.
State & Boundaries
Initializing direction to 1 means starting by moving downwards through rows.
Common Pitfalls & Bugs
Using list multiplication for rows (e.g., [''] * numRows) creates references to the same string, which can cause bugs; however, in this code it is used, which is a subtle issue.
Simulate Zigzag Traversal by Appending Characters and Updating Direction
| 10 | for ch in s: |
| 11 | rows[row] += ch |
| 13 | if row == 0: |
| 14 | direction = 1 |
| 15 | elif row == numRows - 1: |
| 16 | direction = -1 |
| 18 | row += direction |
Objective
To iterate over each character in the input string, append it to the current row, and update the current row index and direction accordingly.
Key Insight
The traversal moves downwards until the bottom row, then reverses to move upwards until the top row, repeating this cycle. By checking boundaries and flipping direction, the algorithm ensures characters are assigned to the correct rows in zigzag order. This simulation avoids building a full matrix and achieves O(n) time.
Interview Quick-Check
Core Logic
The direction variable controls whether the current row index increments or decrements, simulating the zigzag movement.
State & Boundaries
Direction reverses when the current row reaches 0 (top) or numRows - 1 (bottom), ensuring the zigzag pattern.
Common Pitfalls & Bugs
Appending characters to rows before updating direction ensures correct placement; reversing this order can cause off-by-one errors.
Concatenate Row Strings to Form Final Zigzag Converted Output
| 20 | return "".join(rows) |
Objective
To combine all row strings into a single output string representing the zigzag conversion.
Key Insight
After simulating the zigzag traversal, each row string contains characters in the correct order. Concatenating these row strings in sequence produces the final converted string as required by the problem.
Interview Quick-Check
Core Logic
Joining all row strings in order produces the final zigzag string without extra processing.
Complexity
String concatenation using join is efficient and runs in O(n) time.
Line Analysis
This solution has 3 Critical lines interviewers watch for.
if numRows == 1:
Check if numRows is 1 to handle the trivial zigzag case.
When numRows is 1, the zigzag pattern is just the original string, so returning early avoids unnecessary computation and prevents errors related to direction changes.
rows[row] += ch
Append the current character to the string of the current row.
Assigning characters to rows as they appear simulates the zigzag pattern without building a full matrix.
row += direction
Update the current row index by adding the direction.
Incrementing or decrementing the row index simulates moving down or up the zigzag pattern, ensuring characters are assigned to the correct rows.
return s
Return the original string immediately if numRows is 1.
This early return ensures correctness and efficiency for the edge case where no zigzag transformation is needed.
rows = [""] * numRows
Initialize an array of empty strings for each row.
This array will hold characters assigned to each row during the zigzag simulation, enabling efficient appends and final concatenation.
return "".join(rows)
Concatenate all row strings to form the final output.
Joining the row strings in order produces the zigzag converted string as required by the problem.
direction = 1
Initialize the direction to 1, indicating downward movement.
Direction controls whether the current row index increments or decrements, simulating the zigzag traversal through rows.
if row == 0:
Check if the current row is the top row (0).
Reaching the top row requires reversing direction to move downwards in the zigzag pattern.
direction = 1
Set direction to 1 to move downwards.
Changing direction at the top boundary ensures the zigzag traversal moves correctly through rows.
elif row == numRows - 1:
Check if the current row is the bottom row (numRows - 1).
Reaching the bottom row requires reversing direction to move upwards in the zigzag pattern.
direction = -1
Set direction to -1 to move upwards.
Changing direction at the bottom boundary ensures the zigzag traversal moves correctly through rows.
for ch in s:
Iterate over each character in the input string.
Processing characters sequentially is necessary to simulate their placement in the zigzag pattern.
row = 0
Initialize the current row index to 0.
Starting at the top row aligns with the zigzag pattern beginning at the first row.
Test Your Understanding
Why is it necessary to reverse the direction when the current row reaches the top or bottom?
Because the zigzag pattern moves downwards through rows until the bottom row, then reverses to move upwards until the top row, repeating this cycle. Reversing direction at boundaries ensures characters are assigned to rows in the correct zigzag order.
Related Problems
Simulation pattern
Don't just read it. Drill it.
Reconstruct Zigzag Conversion from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.
Unlock the Zigzag Conversion drill