The zigzag pattern places characters top-to-bottom in a column, then diagonally upward until they reach the top row again, then top-to-bottom again, and so on. This creates a "V" shape that repeats.
For numRows = 4, the placement order looks like this:
The characters go down rows 0 through 3, then back up through rows 2 and 1, then down again. That down-and-up cycle has a length of 2 * numRows - 2. For 4 rows, the cycle length is 6. This cycle length drives every efficient solution below.
When numRows = 1 or numRows >= len(s), the zigzag pattern equals the original string and no rearrangement happens. Every solution handles this case with an early return.
1 <= numRows <= 1000 → numRows can exceed the string length. In that case, every character occupies its own row and the output equals the input, which is why each approach starts with the numRows >= len(s) early return.1 <= s.length <= 1000 → The output is a permutation of the input, so the answer always fits in a fixed-width integer index. No overflow concerns arise even with the index arithmetic in Approach 3.Simulate the zigzag directly. Create a 2D grid, place each character in its cell by moving down and then diagonally up, then read the grid row by row.
This mirrors drawing the pattern on paper. It is straightforward to reason about, but it wastes space because most cells in the grid stay empty.
numRows is 1 or greater than or equal to the string length, return the string as-is.numRows rows and n columns, initialized to empty.Trace s = "PAYPALISHIRING" with numRows = 3. The cycle moves down to row 2, then up to row 0, repeating. Each step shows the placed character and the cell it lands in (row, col):
The grid now holds:
Reading row 0 gives PAHN, row 1 gives APLSIIG, row 2 gives YIR. Concatenating produces PAHNAPLSIIGYIR, the expected output.
The bottleneck is the 2D grid. We allocate numRows * n cells, but only n of them ever hold a character. The column of each character is irrelevant to the final answer, since we read row by row. The next approach drops the grid and tracks only each character's row, appending it directly to a per-row list.
Instead of a full 2D grid, use one list (or StringBuilder) per row. While iterating through the string, track which row the current character belongs to and append it there. The row bounces between 0 and numRows-1 exactly as in the simulation, but the wasted columns disappear.
The result is the same reading order as Approach 1, with O(n) space instead of O(n * numRows).
numRows is 1 or greater than or equal to the string length, return the string as-is.numRows empty strings (or StringBuilders).currentRow = 0 and direction = 1 (going down).currentRow.currentRow is 0, set direction to +1 (down). If currentRow is numRows-1, set direction to -1 (up).currentRow += direction.The check order on lines if currentRow == 0 ... else if currentRow == numRows - 1 matters. We append the character first, then decide the direction for the next step. Resetting direction to +1 at the top and -1 at the bottom guarantees the index never moves past row 0 or row numRows-1, so no bounds check is needed when reading rows[currentRow].
Trace s = "PAYPALISHIRING" with numRows = 3. Start at currentRow = 0, direction = 1:
The three rows end up holding PAHN, APLSIIG, and YIR. Concatenating them gives PAHNAPLSIIGYIR.
Approach 2 runs in O(n) time, which is optimal since every character must be read at least once. It still uses O(n) extra space for the row lists. For any given row, the indices of the characters that belong to it follow an arithmetic sequence determined by the cycle length. The next approach computes those indices directly and writes straight to the output, removing the per-row lists.
The zigzag pattern repeats every cycle = 2 * numRows - 2 characters. Let j step through the cycle start indices 0, cycle, 2*cycle, .... Within each cycle, every row receives characters at fixed offsets from j:
jj + (numRows - 1)j + i (downward leg) and j + cycle - i (upward leg)The upward-leg index comes from the geometry of the V. On the way down, row i sits i steps below the top. On the way back up, the same row sits i steps below the top again, which is cycle - i steps after the cycle start, since the full cycle has length cycle. Top and bottom rows lie on the fold of the V, so they appear only once per cycle and have no separate upward character. This lets us iterate row by row and read characters directly from the original string, with no simulation and no extra lists.
numRows is 1 or greater than or equal to the string length, return the string as-is.cycle = 2 * numRows - 2.i from 0 to numRows-1:j = 0, cycle, 2*cycle, ...:s[j + i] if it's within bounds (this is the "down" character).i is not the first or last row, also append s[j + cycle - i] if within bounds (this is the "up" character).Trace s = "PAYPALISHIRING" with numRows = 3, so cycle = 4. The character indices are:
Row 0 (top, down index j only):
Row 0 contributes PAHN.
Row 1 (middle, down index j + 1 and up index j + 4 - 1 = j + 3):
Row 1 contributes APLSIIG.
Row 2 (bottom, down index j + 2 only):
Row 2 contributes YIR.
Concatenating the rows in order gives PAHN + APLSIIG + YIR = PAHNAPLSIIGYIR, matching Approaches 1 and 2.
numRows times, and the inner loop processes characters assigned to that row. Together, they cover all n characters.