diff --git a/problems/0006-zigzag-conversion/analysis.md b/problems/0006-zigzag-conversion/analysis.md new file mode 100644 index 0000000..4874736 --- /dev/null +++ b/problems/0006-zigzag-conversion/analysis.md @@ -0,0 +1,58 @@ +# 0006. Zigzag Conversion + +[LeetCode Link](https://leetcode.com/problems/zigzag-conversion/) + +Difficulty: Medium +Topics: String +Acceptance Rate: 53.4% + +## Hints + +### Hint 1 + +Think about how characters are distributed across rows. Instead of trying to build the zigzag pattern visually, consider collecting all characters that belong to each row separately. + +### Hint 2 + +The zigzag pattern has a repeating cycle. Characters move down through rows 0, 1, 2, ..., numRows-1, then move back up through numRows-2, numRows-3, ..., 1, then down again. Track which direction you're moving (down or up) as you iterate through the string. + +### Hint 3 + +Create an array of strings (or string builders) - one for each row. As you iterate through the input string, append each character to the appropriate row based on the current direction. When you hit the top or bottom row, reverse the direction. + +## Approach + +The key insight is that we don't need to actually construct the 2D zigzag pattern. Instead, we can simulate the zigzag movement and collect characters for each row. + +**Algorithm:** +1. Handle edge cases: if numRows is 1 or the string length is less than numRows, return the original string +2. Create a slice of strings (one for each row) to collect characters +3. Initialize two variables: + - `currentRow` to track which row we're currently on (starts at 0) + - `goingDown` to track whether we're moving down or up (starts as false, will flip to true) +4. Iterate through each character in the string: + - Append the character to the string for `currentRow` + - If we're at the top row (0) or bottom row (numRows-1), flip the direction + - Move to the next row: increment if going down, decrement if going up +5. Concatenate all row strings together and return + +**Example walkthrough with "PAYPALISHIRING", numRows = 3:** +- Row 0: P (↓), A (↑), H (↑), N (↑) +- Row 1: A (↓), P (↓↑), L (↓), S (↓↑), I (↓), I (↓↑), G (↓) +- Row 2: Y (↓), I (↑), R (↑) +- Result: "PAHNAPLSIIGYIR" + +The cycle length is `2 * numRows - 2` (down numRows steps, up numRows-2 steps). + +## Complexity Analysis + +Time Complexity: O(n) where n is the length of the string. We iterate through the string once, and each character is appended to a row exactly once. + +Space Complexity: O(n) for storing the characters in row strings. The final result also requires O(n) space. + +## Edge Cases + +1. **numRows = 1**: No zigzag pattern possible, return the original string +2. **String length ≤ numRows**: The zigzag won't complete a full cycle, but the algorithm handles this naturally +3. **Single character string**: Should return as-is +4. **String shorter than numRows**: Characters only go down, never zigzag back up diff --git a/problems/0006-zigzag-conversion/solution.go b/problems/0006-zigzag-conversion/solution.go new file mode 100644 index 0000000..5ad5780 --- /dev/null +++ b/problems/0006-zigzag-conversion/solution.go @@ -0,0 +1,43 @@ +package main + +// Approach: Simulate the zigzag pattern by collecting characters for each row +// As we iterate through the string, we track which row we're on and whether +// we're moving down or up. When we hit the top or bottom row, we reverse direction. +// Finally, concatenate all rows to get the result. + +func convert(s string, numRows int) string { + // Edge case: if only 1 row or string is too short, no zigzag needed + if numRows == 1 || len(s) <= numRows { + return s + } + + // Create a slice to hold characters for each row + rows := make([]string, numRows) + currentRow := 0 + goingDown := false + + // Iterate through each character and append to appropriate row + for _, char := range s { + rows[currentRow] += string(char) + + // Reverse direction when we hit top or bottom row + if currentRow == 0 || currentRow == numRows-1 { + goingDown = !goingDown + } + + // Move to next row + if goingDown { + currentRow++ + } else { + currentRow-- + } + } + + // Concatenate all rows + result := "" + for _, row := range rows { + result += row + } + + return result +} diff --git a/problems/0006-zigzag-conversion/solution_test.go b/problems/0006-zigzag-conversion/solution_test.go new file mode 100644 index 0000000..f40233d --- /dev/null +++ b/problems/0006-zigzag-conversion/solution_test.go @@ -0,0 +1,64 @@ +package main + +import "testing" + +func TestConvert(t *testing.T) { + tests := []struct { + name string + s string + numRows int + expected string + }{ + { + name: "example 1: PAYPALISHIRING with 3 rows", + s: "PAYPALISHIRING", + numRows: 3, + expected: "PAHNAPLSIIGYIR", + }, + { + name: "example 2: PAYPALISHIRING with 4 rows", + s: "PAYPALISHIRING", + numRows: 4, + expected: "PINALSIGYAHRPI", + }, + { + name: "example 3: single character", + s: "A", + numRows: 1, + expected: "A", + }, + { + name: "edge case: numRows is 1", + s: "ABCDEFGH", + numRows: 1, + expected: "ABCDEFGH", + }, + { + name: "edge case: string length equals numRows", + s: "ABC", + numRows: 3, + expected: "ABC", + }, + { + name: "edge case: string shorter than numRows", + s: "AB", + numRows: 5, + expected: "AB", + }, + { + name: "edge case: two rows", + s: "ABCDE", + numRows: 2, + expected: "ACEBD", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := convert(tt.s, tt.numRows) + if result != tt.expected { + t.Errorf("convert(%q, %d) = %q, want %q", tt.s, tt.numRows, result, tt.expected) + } + }) + } +}