You are given an array of integers distance.
You start at point (0,0) on an X-Y plane and you move distance[0] meters to the north, then distance[1] meters to the west, distance[2] meters to the south, distance[3] meters to the east, and so on. In other words, after each move, your direction changes counter-clockwise.
Return true if your path crosses itself, and false if it does not.
Example 1:
Input: distance = [2,1,1,2]
Output: true
Example 2:
Input: distance = [1,2,3,4]
Output: false
Example 3:
Input: distance = [1,1,1,1]
Output: true
Constraints:
- 1 <= distance.length <= 105
- 1 <= distance[i] <= 105
整体趋势分成两种, 一种是扩大, 即旋转半径持续增大, 一种是收缩, 即旋转半径减小, 如果是一直保持扩大的趋势,那就不会出现交叉的情况。一旦出现收缩的趋势就不可能恢复成扩大,半径一定会持续缩小下去,直到完成最后一步,或者中途出现交叉。 我们把上下左右分别确立边界,不管趋势是放大还是收缩,每走一步都会更新一个边界, 具体来说,是向上走的时候更新下边界,向左走的时候更新右边界, 向下走的时候更新上边界, 向右走的时候更新左边界, 边界的更新整体比移动慢一步, 这样是为了保证我们在判断下一步是否会与边界发生交叉的时候不会出现误判。判断交叉的时候,因为最小距离为 1, 所以除了对边不检查之外, 剩下的三个边界都需要进行检查,即向上走的时候不检查左边界, 向左走的时候不检查下边界, 向下走的时候不检查右边界, 向右走的时候不检查上边界.
impl Solution {
fn next_point(curr: (i32, i32), dir: &str, distance: i32) -> (i32, i32) {
match dir {
"up" => return (curr.0, curr.1 + distance),
"left" => return (curr.0 - distance, curr.1),
"down" => return (curr.0, curr.1 - distance),
"right" => return (curr.0 + distance, curr.1),
_ => unreachable!(),
}
}
fn is_crossing(a_start: (i32, i32), a_end: (i32, i32), b_start: (i32, i32), b_end: (i32, i32)) -> bool {
let a_dir = if a_start.0 == a_end.0 { "vertical" } else { "horizontal" };
let b_dir = if b_start.0 == b_end.0 { "vertical" } else { "horizontal" };
if a_dir == b_dir {
if a_dir == "vertical" {
if a_start.0 != b_start.0 {
return false;
}
let a_min_y = a_start.1.min(a_end.1);
let a_max_y = a_start.1.max(a_end.1);
let b_min_y = b_start.1.min(b_end.1);
let b_max_y = b_start.1.max(b_end.1);
return !(a_max_y < b_min_y || a_min_y > b_max_y);
}
if a_start.1 != b_start.1 {
return false;
}
let a_min_x = a_start.0.min(a_end.0);
let a_max_x = a_start.0.max(a_end.0);
let b_min_x = b_start.0.min(b_end.0);
let b_max_x = b_start.0.max(b_end.0);
return !(a_max_x < b_min_x || a_min_x > b_max_x);
}
if a_dir == "vertical" {
let a_min_y = a_start.1.min(a_end.1);
let a_max_y = a_start.1.max(a_end.1);
let b_min_x = b_start.0.min(b_end.0);
let b_max_x = b_start.0.max(b_end.0);
return a_min_y <= b_start.1 && a_max_y >= b_start.1 && b_min_x <= a_start.0 && b_max_x >= a_start.0;
}
let a_min_x = a_start.0.min(a_end.0);
let a_max_x = a_start.0.max(a_end.0);
let b_min_y = b_start.1.min(b_end.1);
let b_max_y = b_start.1.max(b_end.1);
return a_min_x <= b_start.0 && a_max_x >= b_start.0 && b_min_y <= a_start.1 && b_max_y >= a_start.1;
}
pub fn is_self_crossing(distance: Vec<i32>) -> bool {
let dirs = vec!["up", "left", "down", "right"];
let mut left_border = (0, 0, 0, 0);
let mut right_border = (0, 0, 0, 0);
let mut top_border = (0, 0, 0, 0);
let mut bottom_border = (0, 0, 0, 0);
let mut points = vec![(0, 0)];
let mut trend = "expanding";
for i in 0..distance.len() {
let (next_x, next_y) = Solution::next_point(points[i], &dirs[i % 4], distance[i]);
if trend == "shrinking" {
let start = (points[i].0, points[i].1);
let end = (next_x, next_y);
let left_start = (left_border.0, left_border.1);
let left_end = (left_border.2, left_border.3);
let right_start = (right_border.0, right_border.1);
let right_end = (right_border.2, right_border.3);
let top_start = (top_border.0, top_border.1);
let top_end = (top_border.2, top_border.3);
let bottom_start = (bottom_border.0, bottom_border.1);
let bottom_end = (bottom_border.2, bottom_border.3);
match dirs[i % 4].clone() {
"up" => {
let mut is_crossing = false;
is_crossing |= Solution::is_crossing(right_start, right_end, start, end);
is_crossing |= Solution::is_crossing(bottom_start, bottom_end, start, end);
is_crossing |= Solution::is_crossing(top_start, top_end, start, end);
if is_crossing {
return true;
}
}
"left" => {
let mut is_crossing = false;
is_crossing |= Solution::is_crossing(left_start, left_end, start, end);
is_crossing |= Solution::is_crossing(right_start, right_end, start, end);
is_crossing |= Solution::is_crossing(top_start, top_end, start, end);
if is_crossing {
return true;
}
}
"down" => {
let mut is_crossing = false;
is_crossing |= Solution::is_crossing(left_start, left_end, start, end);
is_crossing |= Solution::is_crossing(bottom_start, bottom_end, start, end);
is_crossing |= Solution::is_crossing(top_start, top_end, start, end);
if is_crossing {
return true;
}
}
"right" => {
let mut is_crossing = false;
is_crossing |= Solution::is_crossing(left_start, left_end, start, end);
is_crossing |= Solution::is_crossing(bottom_start, bottom_end, start, end);
is_crossing |= Solution::is_crossing(right_start, right_end, start, end);
if is_crossing {
return true;
}
}
_ => unreachable!(),
}
}
match dirs[i % 4].clone() {
"up" => {
if next_y <= top_border.1 {
trend = "shrinking";
}
if i > 0 {
bottom_border = (points[i - 1].0, points[i - 1].1, points[i].0, points[i].1);
}
}
"left" => {
if next_x >= left_border.0 {
trend = "shrinking";
}
right_border = (points[i - 1].0, points[i - 1].1, points[i].0, points[i].1);
}
"down" => {
if next_y >= bottom_border.1 {
trend = "shrinking";
}
top_border = (points[i - 1].0, points[i - 1].1, points[i].0, points[i].1);
}
"right" => {
if next_x <= right_border.0 {
trend = "shrinking";
}
left_border = (points[i - 1].0, points[i - 1].1, points[i].0, points[i].1);
}
_ => unreachable!(),
}
points.push((next_x, next_y));
}
false
}
}