1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
//! # 题目描述
//! 
//! 将一个给定字符串 `s` 根据给定的行数 `numRows` ,以从上往下、从左到右进行 Z 字形排列。
//! 
//! 比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:
//! 
//! ```plain
//! P   A   H   N
//! A P L S I I G
//! Y   I   R
//! ```
//! 
//! 之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"。
//! 
//! 请你实现这个将字符串进行指定行数变换的函数:
//! 
//! `string convert(string s, int numRows);`
//! 
//! | 示例 1 |
//! | :-- |
//! | 输入:s = "PAYPALISHIRING", numRows = 3 |
//! | 输出:"PAHNAPLSIIGYIR" |
//! 
//! | 示例 2 |
//! | :-- |
//! | 输入:s = "PAYPALISHIRING", numRows = 4 |
//! | 输出:"PINALSIGYAHRPI" |
//! 
//! 解释:
//! 
//! ```plain
//! P     I    N
//! A   L S  I G
//! Y A   H R
//! P     I
//! ```
//! 
//! | 示例 3 |
//! | :-- |
//! | 输入: s = "A", numRows = 1 |
//! | 输出:"A" |
//! 
//! 提示:
//! 
//! - `1 <= s.length <= 1000`
//! - `s` 由英文字母(小写和大写)、',' 和 '.' 组成
//! - `1 <= numRows <= 1000`
//! 
//! 来源:<https://leetcode.cn/problems/zigzag-conversion>

////////////////////////////////////////////////////////////////////////////////

/// 用于选择要执行的算法的 Enum
pub enum Algorithm {
    /// 使用栈数组实现的字形变换
    STACK = 0,

    /// 使用二维数组实现的字形变换
    MATRIX = 1,
}

/// Z 字形变换
///
/// 可以通过第三个参数选择要使用的算法
///
/// ### Arguments
/// * `s` - 等待变换的字符串
/// * `n_rows` - 变换后的行数
///
/// ```
/// use leetcode_rust::problems_cn::p000_0xx::p000_006::zigzag_conversion;
/// let mut result_value = zigzag_conversion(String::from("PAYPALISHIRING"), 1, None);
/// assert_eq!(result_value, String::from("PAYPALISHIRING"));
/// result_value = zigzag_conversion(String::from("PAYPALISHIRING"), 2, None);
/// assert_eq!(result_value, String::from("PYAIHRNAPLSIIG"));
/// result_value = zigzag_conversion(String::from("PAYPALISHIRING"), 3, None);
/// assert_eq!(result_value, String::from("PAHNAPLSIIGYIR"));
/// ```
pub fn zigzag_conversion(s: String, n_rows: i32, alg: Option<Algorithm>) -> String {
    match alg.unwrap_or(Algorithm::STACK) {
        Algorithm::MATRIX => convert_s1(s, n_rows as usize),
        Algorithm::STACK => convert_s2(s, n_rows),
    }
}

#[cfg(test)]
#[test]
fn test_zigzag_conversion() {
    assert!(zigzag_conversion(String::from("PAPAL"), 1, None) == String::from("PAPAL"));
}

/// Z 字形变换(解法一)
///
/// 使用二维矩阵设计的算法
///
/// ### Arguments
/// * `s` - 等待变换的字符串
/// * `n_cols` - 变换后的列数
#[allow(unused_assignments)]
fn convert_s1(s: String, n_cols: usize) -> String {
    let mut cur_row = 0u16;
    let mut cur_col = 0u16;
    let mut n_rows = 1;

    // Considering max string length 1_000 ASCII characters, minimum 1 column,
    // max row count would be 1_000.
    let mut arr = vec![0u8; n_cols * 1000];
    let mut direction = 0; // 0 for GO, 1 for TURN
    let mut conv_idx = 0usize;
    while conv_idx < s.len() {
        // Set value of current pos
        arr[cur_row as usize * n_cols + cur_col as usize] = s.as_bytes()[conv_idx];
        conv_idx += 1;
        // Calculate next coordinate and direction
        if cur_col == 0 {
            if n_cols > 1 {
                if direction == 1 {
                    direction = 0;
                }
                cur_col += 1;
            } else {
                // Move to next row directly because only 1 column needed.
                cur_row += 1;
                n_rows += 1;
            }
        } else {
            if direction == 0 {
                if cur_col < n_cols as u16 - 1 {
                    // Only move to the right
                    cur_col += 1;
                } else {
                    // Last position, change direction and move to next row
                    direction = 1;
                    cur_col -= 1;
                    cur_row += 1;
                    n_rows += 1;
                }
            } else {
                // Move to left and below
                cur_col -= 1;
                cur_row += 1;
                n_rows += 1;
            }
        }
    }

    conv_idx = 0;
    let mut output = vec![0u8; s.len()];
    cur_row = 0;
    cur_col = 0;
    let mut str_idx = 0;
    while conv_idx < s.len() {
        // Update value if not 0u8
        str_idx = cur_row as usize * n_cols + cur_col as usize;
        if arr[str_idx] != 0u8 {
            output[conv_idx] = arr[str_idx];
            conv_idx += 1;
        }
        // Change position.
        if cur_row < n_rows - 1 {
            cur_row += 1;
        } else {
            cur_row = 0;
            cur_col += 1;
        }
    }
    String::from_utf8(output.to_vec()).unwrap()
}

#[cfg(test)]
#[test]
fn test_conversion_s1() {
    assert!(convert_s1(String::from("PAPAL"), 1) == String::from("PAPAL"));
}

/// Z 字形变换(解法二)
///
/// 使用栈数组设计的算法
///
/// ### Arguments
/// * `s` - 等待变换的字符串
/// * `n_rows` - 变换后的行数
fn convert_s2(s: String, num_rows: i32) -> String {
    let mut rows = vec![String::new(); num_rows as usize];

    let mut cur_row: i32 = 0;
    let mut go_down: bool = true;
    for val in s.bytes() {
        rows[cur_row as usize].push(val as char);
        if !go_down && cur_row == 0 {
            go_down = true;
        } else if go_down && cur_row == num_rows - 1 {
            go_down = false;
        }

        if go_down {
            cur_row += 1;
        } else {
            cur_row -= 1;
        }

        cur_row = i32::min(num_rows - 1, cur_row);
        cur_row = i32::max(0, cur_row);
    }

    rows.join("").to_string()
}

#[cfg(test)]
#[test]
fn test_conversion_s2() {
    assert!(convert_s2(String::from("PAPAL"), 2) == String::from("PPLAA"));
}