Skip to content

Commit 44ed034

Browse files
committed
Update: complete p#8 and its documentation
1 parent a492206 commit 44ed034

File tree

3 files changed

+148
-47
lines changed

3 files changed

+148
-47
lines changed

src/problems/p000_0xx/p000_008.rs

Lines changed: 113 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
//!
3232
//!
3333
//! Example 1:
34-
//! ```ignore
34+
//! ```plain
3535
//! Input: s = "42"
3636
//! Output: 42
3737
//! Explanation: The underlined characters are what is read in, the caret is the
@@ -47,7 +47,7 @@
4747
//! ```
4848
//!
4949
//! Example 2:
50-
//! ```ignore
50+
//! ```plain
5151
//! Input: s = " -42"
5252
//! Output: -42
5353
//! Explanation:
@@ -62,7 +62,7 @@
6262
//! ```
6363
//!
6464
//! Example 3:
65-
//! ```
65+
//! ```plain
6666
//! Input: s = "4193 with words"
6767
//! Output: 4193
6868
//! Explanation:
@@ -89,75 +89,161 @@
8989
///
9090
/// # Argument
9191
/// * `s` - input string
92+
///
93+
/// ```
94+
/// use leetcode_rust::problems::p000_0xx::p000_008::my_atoi;
95+
///
96+
/// assert!(my_atoi(String::from("-12.5")) == -12);
97+
/// ```
9298
pub fn my_atoi(s: String) -> i32 {
99+
// Upper bound for possitive i32. The last element should be increased
100+
// if given string indicates a negative i32 (no sign symbol included).
93101
let mut threshold_val: [u8; 10] = [50, 49, 52, 55, 52, 56, 51, 54, 52, 55];
102+
103+
// Indicates whether the given string starts with a negative integer.
94104
let mut is_negative = false;
105+
106+
// Indicates existence of digits in given string.
95107
let mut has_digits = false;
108+
109+
// Current index position during scanning of input string.
96110
let mut curr_idx: usize = 0;
111+
112+
// Indicates whether scanned part should be treated as an integer.
97113
let mut is_started = false;
114+
115+
// Temp value during looping and return value after looping complete.
98116
let mut val: i32 = 0;
117+
118+
// Bytes form of input string. For faster comparison and computation.
99119
let s_bytes = s.as_bytes();
120+
121+
// Indicates whether the scanned part correspond to an overflowed interger.
100122
let mut is_overflow = false;
123+
124+
// In some cases, we should just ignore overflow detection because the
125+
// parsed digits are simply less than same digit in possitive / negative
126+
// overflow threshold value respectively.
127+
let mut is_overflow_ignored = false;
128+
129+
// Used to check if given string starts with exactly same digits comparing
130+
// with threshold.
101131
let mut is_full_match = true;
132+
133+
// A vector containing all parsed and valid digits.
102134
let mut val_vec: Vec<u8> = vec![];
135+
136+
// Loop forever.
137+
// Looping through all characters in sequence or escape during looping are
138+
// controlled by additional flags.
103139
loop {
140+
// Guard condition, exit looping when no more characters to check.
104141
if curr_idx == s_bytes.len() {
105142
break;
106143
}
107144

108145
if s_bytes[curr_idx] == 32 {
146+
// This is a whitespace character, check its position.
109147
if !is_started {
110-
// Leading whitespace, ignore
148+
// Leading whitespace, ignore it
111149
curr_idx += 1;
112150
continue;
113151
} else {
114-
// None leading whitespace, end of reading
152+
// None leading whitespace, end of reading because we already
153+
// found some significant digits.
115154
break;
116155
}
117156
}
118157

119-
if !is_started && [43u8, 45u8].contains(&s_bytes[curr_idx]) {
158+
if [43u8, 45u8].contains(&s_bytes[curr_idx]) {
159+
// Check positive and negative signs.
120160
if has_digits {
121-
// Signs after digits (even 0 not allowed)
161+
// Signs after digits (even after 0 is not allowed)
162+
// e.g. `12-222`, `0+123`
122163
break;
123164
}
124-
if s_bytes[curr_idx] == 45 {
125-
is_negative = true;
126-
threshold_val = [50, 49, 52, 55, 52, 56, 51, 54, 52, 56];
165+
if !is_started {
166+
// Should only adjust sign when this is the very first valid
167+
// symbol in the given string.
168+
if s_bytes[curr_idx] == 45 {
169+
// Adjust flag and set new overflow threshold.
170+
is_negative = true;
171+
threshold_val = [50, 49, 52, 55, 52, 56, 51, 54, 52, 56];
172+
}
173+
// Setup flag to avoid `-+` sequences.
174+
is_started = true;
175+
curr_idx += 1;
176+
continue;
127177
}
128-
is_started = true;
129-
curr_idx += 1;
130-
continue;
131178
}
132179

180+
// Now parse digit and detect overflow.
133181
if 48 <= s_bytes[curr_idx] && s_bytes[curr_idx] <= 57 {
182+
// Once a new digit found, update related flags to avoid malformed
183+
// sequences like: `0 123` and `123-6`.
134184
has_digits = true;
185+
is_started = true;
186+
135187
if val == 0 && s_bytes[curr_idx] == 48 {
136188
// Skip leading zeros.
137189
curr_idx += 1;
138190
continue;
139191
}
140192

141193
// Digits
142-
for idx in 0..val_vec.len() {
194+
if val_vec.len() >= threshold_val.len() - 1 {
195+
// Only check overflow when parsed digits are the same or 1
196+
// digit shorter than threshold. Otherwise it could waste
197+
// execution time.
198+
199+
// The following traversal checks:
200+
// 1. if parsed part has exact same digits as threshold;
201+
// 2. if parsed part should be ignored in future checking.
202+
for idx in 0..val_vec.len() {
203+
if !is_overflow {
204+
// Save time
205+
if !is_overflow_ignored && val_vec[idx] > threshold_val[idx] {
206+
// One digit is greater than threshold, no need to
207+
// check later ones.
208+
is_overflow = true;
209+
is_full_match = false;
210+
break;
211+
} else if val_vec[idx] < threshold_val[idx] {
212+
// One digit is smaller than threshold, means as
213+
// long as its shorter than threshold, it cannot
214+
// overflow.
215+
// But it still needs testing for its length
216+
// in a later step.
217+
is_full_match = false;
218+
is_overflow_ignored = true;
219+
}
220+
}
221+
}
222+
143223
if !is_overflow {
144-
if val_vec[idx] > threshold_val[idx] {
224+
// Test for current digit:
225+
// 1. if it extends length of parsed number that causes
226+
// overflow.
227+
// 2. if it increases parsed number that causes overflow.
228+
if val_vec.len() == threshold_val.len() - 1 {
229+
// Check if adding this digit causes overflow
230+
if is_full_match
231+
&& s_bytes[curr_idx] > threshold_val[threshold_val.len() - 1]
232+
{
233+
is_overflow = true;
234+
break;
235+
}
236+
} else if val_vec.len() == threshold_val.len() {
237+
// Check if extending parsed part causes overflow.
145238
is_overflow = true;
146-
is_full_match = false;
147239
break;
148-
} else if val_vec[idx] < threshold_val[idx] {
149-
is_full_match = false;
150240
}
151241
}
152242
}
153-
if !is_overflow {
154-
// Check if adding this digit causes overflow
155-
if is_full_match || val_vec.len() == threshold_val.len() {
156-
is_overflow = true;
157-
break;
158-
}
243+
if is_overflow {
244+
// Do NOT prepend current digit to parsed number.
245+
break;
159246
}
160-
161247
val = val * 10 + (s_bytes[curr_idx] - 48) as i32;
162248
val_vec.push(s_bytes[curr_idx]);
163249
curr_idx += 1;
@@ -166,6 +252,8 @@ pub fn my_atoi(s: String) -> i32 {
166252

167253
break;
168254
}
255+
// The following steps simply determines what value to return by
256+
// overflowing flag and integer sign.
169257
if is_overflow {
170258
if is_negative {
171259
-2147483648

tests/cases/c000_0xx/c000_008.rs

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,40 @@
11
use leetcode_rust::common::{Case, CaseGroup};
22

33
pub fn use_cases() -> Vec<Case<String, i32, i32>> {
4-
let mut case_group: CaseGroup<String,i32,i32> = CaseGroup::new();
5-
case_group.add(Case::new(String::from("-12"), vec![-12]));
6-
case_group.add(Case::new(String::from(" -12"), vec![-12]));
7-
case_group.add(Case::new(String::from("- 12"), vec![0]));
8-
case_group.add(Case::new(String::from("+12"), vec![12]));
9-
case_group.add(Case::new(String::from("1.2"), vec![1]));
10-
case_group.add(Case::new(String::from("1200"), vec![1200]));
11-
case_group.add(Case::new(String::from("- +12"), vec![0]));
12-
case_group.add(Case::new(String::from("-1 +12"), vec![-1]));
13-
case_group.add(Case::new(String::from("-1.+12"), vec![-1]));
14-
case_group.add(Case::new(String::from("2147483647"), vec![2147483647]));
15-
case_group.add(Case::new(String::from("2147483648"), vec![2147483647]));
16-
case_group.add(Case::new(String::from("21474836471"), vec![2147483647]));
17-
case_group.add(Case::new(String::from("-2147483649"), vec![-2147483648]));
18-
case_group.add(Case::new(String::from("-6 with words"), vec![-6]));
19-
case_group.add(Case::new(String::from("-91283472332"), vec![-2147483648]));
20-
case_group.add(Case::new(String::from("-21474836460"), vec![-2147483648]));
21-
case_group.add(Case::new(String::from("21474836460"), vec![2147483647]));
22-
case_group.add(Case::new(String::from(" 0000000000012345678"),vec![12345678],));
23-
case_group.add(Case::new(String::from("00000-42a1234"), vec![0]));
24-
case_group.add(Case::new(String::from("2147483646"), vec![2147483646]));
4+
let mut case_group: CaseGroup<String, i32, i32> = CaseGroup::new();
5+
6+
// #1
7+
case_group.create("-12", vec![-12]);
8+
case_group.create(" -12", vec![-12]);
9+
case_group.create("- 12", vec![0]);
10+
case_group.create("+12", vec![12]);
11+
case_group.create("1.2", vec![1]);
12+
// #6
13+
case_group.create("1200", vec![1200]);
14+
case_group.create("- +12", vec![0]);
15+
case_group.create("-1 +12", vec![-1]);
16+
case_group.create("-1.+12", vec![-1]);
17+
case_group.create("2147483647", vec![2147483647]);
18+
// #11
19+
case_group.create("2147483648", vec![2147483647]);
20+
case_group.create("21474836471", vec![2147483647]);
21+
case_group.create("-2147483649", vec![-2147483648]);
22+
case_group.create("-6 with words", vec![-6]);
23+
case_group.create("-91283472332", vec![-2147483648]);
24+
// #16
25+
case_group.create("-21474836460", vec![-2147483648]);
26+
case_group.create("21474836460", vec![2147483647]);
27+
case_group.create(" 0000000000012345678", vec![12345678]);
28+
case_group.create("00000-42a1234", vec![0]);
29+
case_group.create("2147483646", vec![2147483646]);
30+
// #21
31+
case_group.create("42", vec![42]);
32+
case_group.create(" -42", vec![-42]);
33+
case_group.create("4193 with words", vec![4193]);
34+
case_group.create("0 123", vec![0]);
35+
case_group.create("with words 121", vec![0]);
36+
// #26
37+
case_group.create("-+12", vec![0]);
2538

2639
case_group.all()
2740
}

tests/problems/p000_0xx.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ fn p000_007_reverse_integer() {
3939
#[test]
4040
fn p000_008_convert_string_to_integer() {
4141
for case in c000_008::use_cases() {
42-
// if case.label() == String::from("11") {
42+
// if case.label() == String::from("24") {
4343
case.is_valid(p000_008::my_atoi((&case.input).to_string()));
4444
// }
4545
}

0 commit comments

Comments
 (0)