6. Zigzag Conversion - Explanation

Problem Link

Description

You are given a string s and a positive integer numRows. Return the string after converting it to zig-zag pattern.

The string "GOOGLEISHIRING" is written in a zigzag pattern on a given number of rows numRows=3 like this: (you may want to display this pattern in a fixed font for better legibility)

G   L   H   N
O G E S I I G
O   I   R

And then read line by line: "GLHNOGESIIGOIR"

Example 1:

Input: s = "GOOGLEISHIRING", numRows = 4

Output: "GINOESIGOLHRGI"

Explanation:

G    I    N
O  E S  I G
O L  H R
G    I

Example 2:

Input: s = "GOOGLEISHIRING", numRows = 5

Output: "GHOSIOIRGEIGLN"

Explanation:

G     H
O   S I
O  I  R
G E   I G
L     N

Constraints:

  • 1 <= s.length, numRows <= 1000
  • s consists of English letters (lower-case and upper-case), ',' and '.'.


Topics

Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • String Manipulation - Building strings from characters and concatenating multiple strings
  • Pattern Recognition - Identifying mathematical formulas to calculate character positions in repeating patterns
  • Array/List Operations - Using multiple lists to collect characters row by row

1. Iteration - I

Intuition

When writing characters in a zigzag pattern, each row follows a predictable spacing pattern. The key insight is that the distance between characters in the same row follows a cycle of length 2 * (numRows - 1). For the first and last rows, characters appear at regular intervals of this cycle length. For middle rows, there are two characters per cycle: one at the regular position and one at a calculated offset within the cycle.

Algorithm

  1. Handle the edge case where numRows is 1 by returning the original string.
  2. Calculate the base increment as 2 * (numRows - 1), which represents one full zigzag cycle.
  3. For each row r from 0 to numRows - 1:
    • Start at index r and jump by the increment to collect characters at regular cycle positions.
    • For middle rows (not first or last), also collect the "diagonal" character at position i + increment - 2 * r if it exists.
  4. Concatenate all collected characters and return the result.
class Solution:
    def convert(self, s: str, numRows: int) -> str:
        if numRows == 1:
            return s

        res = []
        for r in range(numRows):
            increment = 2 * (numRows - 1)
            for i in range(r, len(s), increment):
                res.append(s[i])
                if r > 0 and r < numRows - 1 and i + increment - 2 * r < len(s):
                    res.append(s[i + increment - 2 * r])

        return ''.join(res)

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n) for the ouput string.

2. Iteration - II

Intuition

Instead of calculating positions mathematically, we can simulate the actual zigzag writing process. We maintain a current row and a direction. As we traverse the string, we place each character in its corresponding row. When we hit the top or bottom row, we reverse direction. This approach directly models how characters would be written in the zigzag pattern.

Algorithm

  1. Handle edge cases where numRows is 1 or greater than or equal to the string length by returning the original string.
  2. Create an array of lists (or strings), one for each row.
  3. Initialize the current row to 0 and direction to 1 (moving down).
  4. For each character in the string:
    • Append the character to the current row's list.
    • Move to the next row by adding the direction.
    • If we reach the first or last row, reverse the direction by multiplying by -1.
  5. Concatenate all rows in order and return the result.
class Solution:
    def convert(self, s: str, numRows: int) -> str:
        if numRows == 1 or numRows >= len(s):
            return s

        res = [[] for _ in range(numRows)]
        row, dir = 0, 1
        for c in s:
            res[row].append(c)
            row += dir
            if row == 0 or row == (numRows - 1):
                dir *= -1

        return ''.join([''.join(row) for row in res])

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n) for the output string.

Common Pitfalls

Not Handling the Single Row Edge Case

When numRows == 1, the zigzag pattern is just the original string. Without this check, the increment becomes 2 * (1 - 1) = 0, causing an infinite loop or division issues.

# Wrong: Missing edge case
increment = 2 * (numRows - 1)  # increment = 0 when numRows = 1
for i in range(r, len(s), increment):  # Infinite loop!

Incorrect Direction Toggle Logic

When simulating the zigzag traversal, the direction should only flip when reaching the first or last row. A common mistake is toggling direction after every step, or only checking one boundary.

# Wrong: Toggling every time or only checking one boundary
row += dir
if row == numRows - 1:  # Misses the top boundary!
    dir *= -1

Wrong Index Calculation for Middle Row Diagonal Characters

In the mathematical approach, middle rows have two characters per cycle. The diagonal character position i + increment - 2 * r is often miscalculated, leading to missing characters or index out of bounds errors.

# Wrong: Using incorrect offset formula
if r > 0 and r < numRows - 1:
    res.append(s[i + increment - r])  # Should be: i + increment - 2 * r