Problem overview
The function print_x receives a positive integer n as an i32 and prints a square of n lines with n characters each. The two diagonals of the square are drawn with x and every other cell is a -.
For n = 5 the output is:
x---x
-x-x-
--x--
-x-x-
x---x
Each line has two x's, except the middle line, where the two diagonals cross and there is only one.
Approach: follow the two diagonals
Looking at the picture line by line, the x's move predictably. On line 0 they sit at the two ends, at columns 0 and n - 1. On each following line the left x moves one column to the right and the right x moves one column to the left. They are two points walking toward each other, which is exactly what the two diagonals of a square are.
So the program keeps two numbers, the column of the first x and the column of the second, and updates them after each line. To print a line, it walks over the columns 0 to n - 1 and writes x when the column equals either number and - otherwise.
When the two numbers meet in the middle of an odd square, they are equal. The test "column equals first or second" is still true for that one column, so the middle line gets one x without any special case. Past the middle the two numbers cross over, and the same test keeps working with the roles swapped.
Here is the state for n = 5 as the loop runs. The column positions are those used when the line is built:
| Line | first_x | second_x | Printed |
|---|---|---|---|
| 0 | 0 | 4 | x---x |
| 1 | 1 | 3 | -x-x- |
| 2 | 2 | 2 | --x-- |
| 3 | 3 | 1 | -x-x- |
| 4 | 4 | 0 | x---x |
On line 3 the first number is larger than the second. That doesn't matter, because the test only asks whether a column equals one of them, and it never compares the two numbers with each other.
Step by step
- Declare the two columns with
let mut:first_xstarts at 0 andsecond_xstarts atn - 1. They are mutable because they move on every line. - Loop once per line with
for _ in 0..n. The range excludesn, so the body runsntimes, and the loop variable is not used, so it is named_. - Print one line. Inside the loop, call a helper
build_string(n, first_x, second_x)withSelf::build_stringand print the result withprintln!. The lesson writes the helper afterwards. - Move the x's. After printing, add 1 to
first_xand subtract 1 fromsecond_x. - Write the helper's signature above
print_x: threei32parameters,n,first_xandsecond_x, and aStringresult. - Create the result as an empty mutable
Stringnamedresult. - Loop over the columns with
for index in 0..n. The index takes the values 0 ton - 1. - Choose the character. If
index == first_x || index == second_x, push'x'; otherwise push'-'. The operator||is true when at least one side is true. - Return
resultafter the loop, once every column has been filled.
The solution in Rust
impl Solution {
fn build_string(n: i32, first_x: i32, second_x: i32) -> String {
let mut result = String::new();
for index in 0..n {
if index == first_x || index == second_x {
result.push('x');
} else {
result.push('-');
}
}
return result;
}
fn print_x(n: i32) {
let mut first_x = 0;
let mut second_x = n - 1;
for _ in 0..n {
println!("{}", Self::build_string(n, first_x, second_x));
first_x += 1;
second_x -= 1;
}
}
}
The Rust details in these lines:
- No
++or--. Rust has no increment or decrement operators. Use+= 1and-= 1. - Inferred types.
first_xandsecond_xare not annotated, but they are passed where ani32is expected and compared withindex, so Rust infersi32. - Signed on purpose. After the last line has been printed,
second_xhas dropped to -1, whatevernis. Ausizecould not hold that, and in a debug build the subtraction would panic. Withi32the value is just a number that matches no column, and it is never used again. - Chars, not strings.
'x'and'-'in single quotes arecharvalues, which is whatString::pushtakes. - Calling the helper. The helper has no
self, so the call isSelf::build_string(...), and the returnedStringgoes straight intoprintln!. - Explicit
return.return result;ends the function. Dropping the wordreturnand the semicolon would mean the same thing.
Complexity
Time: O(n²). The outer loop makes n lines, and building each line visits n columns. The output itself contains n² characters, so no solution can be faster.
Space: O(n). Only one line of n characters is held at a time.
Edge cases and common mistakes
n = 1. Both columns are 0, so the single line isx.- Even
n. The columns never coincide; they swap places in the middle instead. Forn = 4the output isx--x,-xx-,-xx-,x--x, and forn = 2it isxxtwice. The same code handles both parities. - Starting at 1 instead of 0. Columns here are 0-based, since
indexstarts at 0. Startingfirst_xat 1 andsecond_xatnshifts the whole picture and drops an x from the first line. - Moving the x's before printing. The first line would already be the second one.
- Stopping the inner loop at
n - 1. The last column would never be written, and the lines would be one character short. - Using
&&instead of||. A column can't equal two different numbers, so the line would be all dashes except in the middle.
Testing
n = 1printsx.n = 2printsxxandxx.n = 3printsx-x,-x-,x-x.n = 4printsx--x,-xx-,-xx-,x--x.
