在计算机图形学和几何计算中,识别和计数矩形是一个经典问题。在 Exercism 的 "rectangles" 练习中,我们需要在一个由 ASCII 字符组成的网格中识别并计数所有可能的矩形。这不仅能帮助我们掌握二维数组处理和几何算法,还能深入学习Rust中的迭代器、模式匹配和复杂逻辑处理。
什么是矩形计数问题?
矩形计数问题要求我们在一个由特定字符组成的网格中找出所有由 '+'、'-' 和 '|' 字符构成的矩形。一个有效的矩形必须满足以下条件:
- 四个角由 '+' 字符表示
- 水平边由 '-' 字符组成
- 垂直边由 '|' 字符组成
- 矩形的边必须是连续的
例如,以下是一个包含两个矩形的网格:
+-+
| |
+-+-+
| |
+-+
让我们先看看练习提供的函数签名:
rust
pub fn count(lines: &[&str]) -> u32 {
unimplemented!("\nDetermine the count of rectangles in the ASCII diagram represented by the following lines:\n{:#?}\n.", lines);
}
我们需要实现 count 函数,使其能够计算给定 ASCII 网格中矩形的数量。
设计分析
1. 核心要求
- 网格解析:正确解析由字符串切片组成的二维网格
- 角点识别:识别所有可能的矩形角点('+' 字符)
- 矩形验证:验证由四个角点组成的图形是否为有效矩形
- 计数统计:统计所有有效矩形的数量
2. 技术要点
- 二维坐标系统:理解和操作二维网格坐标
- 字符匹配:识别和验证特定字符
- 几何算法:实现矩形识别算法
- 性能优化:优化搜索和验证过程
完整实现
1. 基础实现
rust
pub fn count(lines: &[&str]) -> u32 {
if lines.is_empty() {
return 0;
}
let height = lines.len();
let width = lines.iter().map(|line| line.len()).max().unwrap_or(0);
if width == 0 {
return 0;
}
// 将输入转换为二维字符数组以便处理
let grid: Vec<Vec<char>> = lines
.iter()
.map(|line| line.chars().collect())
.collect();
let mut count = 0;
// 寻找所有可能的左上角点
for top in 0..height {
for left in 0..width {
// 检查是否为左上角(必须是 '+')
if get_char(&grid, top, left) != Some('+') {
continue;
}
// 寻找所有可能的右下角点
for bottom in (top + 1)..height {
for right in (left + 1)..width {
// 检查是否能构成矩形的四个角
if get_char(&grid, top, right) == Some('+') && // 右上角
get_char(&grid, bottom, left) == Some('+') && // 左下角
get_char(&grid, bottom, right) == Some('+') && // 右下角
is_horizontal_line(&grid, top, left, right) && // 顶边
is_horizontal_line(&grid, bottom, left, right) && // 底边
is_vertical_line(&grid, left, top, bottom) && // 左边
is_vertical_line(&grid, right, top, bottom) { // 右边
count += 1;
}
}
}
}
}
count
}
// 获取指定位置的字符
fn get_char(grid: &[Vec<char>], row: usize, col: usize) -> Option<char> {
if row < grid.len() && col < grid[row].len() {
Some(grid[row][col])
} else {
None
}
}
// 检查水平线段是否有效
fn is_horizontal_line(grid: &[Vec<char>], row: usize, start_col: usize, end_col: usize) -> bool {
for col in start_col..=end_col {
match get_char(grid, row, col) {
Some('-') | Some('+') => continue,
_ => return false,
}
}
true
}
// 检查垂直线段是否有效
fn is_vertical_line(grid: &[Vec<char>], col: usize, start_row: usize, end_row: usize) -> bool {
for row in start_row..=end_row {
match get_char(grid, row, col) {
Some('|') | Some('+') => continue,
_ => return false,
}
}
true
}
2. 优化实现
rust
pub fn count(lines: &[&str]) -> u32 {
if lines.is_empty() {
return 0;
}
let height = lines.len();
let width = lines.iter().map(|line| line.len()).max().unwrap_or(0);
if width == 0 {
return 0;
}
// 预处理:找出所有 '+' 字符的位置
let mut corners = Vec::new();
let grid: Vec<Vec<char>> = lines
.iter()
.enumerate()
.map(|(row, line)| {
line.chars()
.enumerate()
.map(|(col, ch)| {
if ch == '+' {
corners.push((row, col));
}
ch
})
.collect()
})
.collect();
let mut rectangle_count = 0;
// 对于每一对可能的对角线点
for &(top, left) in &corners {
for &(bottom, right) in &corners {
// 确保是有效的对角线(右下角在左上角的右下方)
if top >= bottom || left >= right {
continue;
}
// 检查其他两个角点是否存在
if !corners.contains(&(top, right)) || !corners.contains(&(bottom, left)) {
continue;
}
// 验证四条边
if is_valid_horizontal_line(&grid, top, left, right) &&
is_valid_horizontal_line(&grid, bottom, left, right) &&
is_valid_vertical_line(&grid, left, top, bottom) &&
is_valid_vertical_line(&grid, right, top, bottom) {
rectangle_count += 1;
}
}
}
rectangle_count
}
// 检查水平线段是否有效(只包含 '+' 或 '-')
fn is_valid_horizontal_line(grid: &[Vec<char>], row: usize, start_col: usize, end_col: usize) -> bool {
for col in start_col..=end_col {
match grid.get(row).and_then(|row_vec| row_vec.get(col)) {
Some(&('-' | '+')) => continue,
_ => return false,
}
}
true
}
// 检查垂直线段是否有效(只包含 '+' 或 '|')
fn is_valid_vertical_line(grid: &[Vec<char>], col: usize, start_row: usize, end_row: usize) -> bool {
for row in start_row..=end_row {
match grid.get(row).and_then(|row_vec| row_vec.get(col)) {
Some(&('|' | '+')) => continue,
_ => return false,
}
}
true
}
3. 使用迭代器的函数式实现
rust
pub fn count(lines: &[&str]) -> u32 {
if lines.is_empty() {
return 0;
}
let grid: Vec<Vec<char>> = lines
.iter()
.map(|line| line.chars().collect())
.collect();
let height = grid.len();
let width = grid.iter().map(Vec::len).max().unwrap_or(0);
if width == 0 {
return 0;
}
// 找出所有角点
let corners: Vec<(usize, usize)> = (0..height)
.flat_map(|row| {
(0..width).filter_map(move |col| {
if get_char_safe(&grid, row, col) == Some('+') {
Some((row, col))
} else {
None
}
})
})
.collect();
// 计算矩形数量
corners
.iter()
.flat_map(|&(top, left)| {
corners.iter().filter_map(move |&(bottom, right)| {
if top < bottom && left < right {
Some((top, left, bottom, right))
} else {
None
}
})
})
.filter(|&(top, left, bottom, right)| {
// 检查其他两个角点
get_char_safe(&grid, top, right) == Some('+') &&
get_char_safe(&grid, bottom, left) == Some('+') &&
// 验证四条边
is_valid_horizontal_line(&grid, top, left, right) &&
is_valid_horizontal_line(&grid, bottom, left, right) &&
is_valid_vertical_line(&grid, left, top, bottom) &&
is_valid_vertical_line(&grid, right, top, bottom)
})
.count() as u32
}
fn get_char_safe(grid: &[Vec<char>], row: usize, col: usize) -> Option<char> {
grid.get(row).and_then(|row_vec| row_vec.get(col)).copied()
}
fn is_valid_horizontal_line(grid: &[Vec<char>], row: usize, start_col: usize, end_col: usize) -> bool {
(start_col..=end_col).all(|col| {
matches!(get_char_safe(grid, row, col), Some('-' | '+'))
})
}
fn is_valid_vertical_line(grid: &[Vec<char>], col: usize, start_row: usize, end_row: usize) -> bool {
(start_row..=end_row).all(|row| {
matches!(get_char_safe(grid, row, col), Some('|' | '+'))
})
}
测试用例分析
通过查看测试用例,我们可以更好地理解需求:
rust
#[test]
fn test_zero_area_1() {
let lines = &[];
assert_eq!(0, count(lines))
}
空输入应该返回0。
rust
#[test]
fn test_zero_area_2() {
let lines = &[""];
assert_eq!(0, count(lines))
}
空字符串输入应该返回0。
rust
#[test]
fn test_empty_area() {
let lines = &[" "];
assert_eq!(0, count(lines))
}
只包含空格的输入应该返回0。
rust
#[test]
fn test_one_rectangle() {
#[rustfmt::skip]
let lines = &[
"+-+",
"| |",
"+-+",
];
assert_eq!(1, count(lines))
}
一个标准矩形应该返回1。
rust
#[test]
fn test_two_rectangles_no_shared_parts() {
#[rustfmt::skip]
let lines = &[
" +-+",
" | |",
"+-+-+",
"| | ",
"+-+ ",
];
assert_eq!(2, count(lines))
}
两个不共享部分的矩形应该返回2。
rust
#[test]
fn test_five_rectangles_three_regions() {
#[rustfmt::skip]
let lines = &[
" +-+",
" | |",
"+-+-+",
"| | |",
"+-+-+",
];
assert_eq!(5, count(lines))
}
复杂布局中应该正确计数所有矩形。
rust
#[test]
fn rectangle_of_height_1() {
#[rustfmt::skip]
let lines = &[
"+--+",
"+--+",
];
assert_eq!(1, count(lines))
}
高度为1的矩形应该被正确识别。
rust
#[test]
fn rectangle_of_width_1() {
#[rustfmt::skip]
let lines = &[
"++",
"||",
"++",
];
assert_eq!(1, count(lines))
}
宽度为1的矩形应该被正确识别。
rust
#[test]
fn unit_square() {
#[rustfmt::skip]
let lines = &[
"++",
"++",
];
assert_eq!(1, count(lines))
}
单位正方形应该被正确识别。
rust
#[test]
fn test_incomplete_rectangles() {
#[rustfmt::skip]
let lines = &[
" +-+",
" |",
"+-+-+",
"| | -",
"+-+-+",
];
assert_eq!(1, count(lines))
}
不完整的矩形不应该被计数。
rust
#[test]
fn test_complicated() {
let lines = &[
"+------+----+",
"| | |",
"+---+--+ |",
"| | |",
"+---+-------+",
];
assert_eq!(3, count(lines))
}
复杂布局应该正确计数。
rust
#[test]
fn test_not_so_complicated() {
let lines = &[
"+------+----+",
"| | |",
"+------+ |",
"| | |",
"+---+-------+",
];
assert_eq!(2, count(lines))
}
另一个复杂布局的测试。
rust
#[test]
fn test_large_input_with_many_rectangles() {
let lines = &[
"+---+--+----+",
"| +--+----+",
"+---+--+ |",
"| +--+----+",
"+---+--+--+-+",
"+---+--+--+-+",
"+------+ | |",
" +-+",
];
assert_eq!(60, count(lines))
}
大型输入应该正确计数所有矩形。
性能优化版本
考虑性能的优化实现:
rust
pub fn count(lines: &[&str]) -> u32 {
if lines.is_empty() {
return 0;
}
let height = lines.len();
// 预先计算每行的长度以避免重复计算
let widths: Vec<usize> = lines.iter().map(|line| line.len()).collect();
let max_width = *widths.iter().max().unwrap_or(&0);
if max_width == 0 {
return 0;
}
// 使用预分配的向量以提高性能
let mut grid: Vec<Vec<char>> = Vec::with_capacity(height);
let mut corners: Vec<(usize, usize)> = Vec::new();
// 一次遍历完成网格构建和角点收集
for (row, line) in lines.iter().enumerate() {
let mut row_chars = Vec::with_capacity(line.len());
for (col, ch) in line.chars().enumerate() {
if ch == '+' {
corners.push((row, col));
}
row_chars.push(ch);
}
grid.push(row_chars);
}
let mut count = 0;
// 使用索引而不是迭代器以提高性能
for i in 0..corners.len() {
let (top, left) = corners[i];
for j in (i + 1)..corners.len() {
let (bottom, right) = corners[j];
// 确保是有效的对角线
if top >= bottom || left >= right {
continue;
}
// 检查其他两个角点(使用二分查找可能更快,但对于小数据集线性搜索足够)
if !corners.contains(&(top, right)) || !corners.contains(&(bottom, left)) {
continue;
}
// 验证四条边
if is_valid_horizontal_line_optimized(&grid, top, left, right) &&
is_valid_horizontal_line_optimized(&grid, bottom, left, right) &&
is_valid_vertical_line_optimized(&grid, left, top, bottom) &&
is_valid_vertical_line_optimized(&grid, right, top, bottom) {
count += 1;
}
}
}
count
}
fn is_valid_horizontal_line_optimized(
grid: &[Vec<char>],
row: usize,
start_col: usize,
end_col: usize
) -> bool {
let row_vec = &grid[row];
for col in start_col..=end_col {
if col >= row_vec.len() {
return false;
}
match row_vec[col] {
'-' | '+' => continue,
_ => return false,
}
}
true
}
fn is_valid_vertical_line_optimized(
grid: &[Vec<char>],
col: usize,
start_row: usize,
end_row: usize
) -> bool {
for row in start_row..=end_row {
if row >= grid.len() || col >= grid[row].len() {
return false;
}
match grid[row][col] {
'|' | '+' => continue,
_ => return false,
}
}
true
}
// 使用位图优化的版本
pub fn count_with_bitset(lines: &[&str]) -> u32 {
if lines.is_empty() {
return 0;
}
let height = lines.len();
let widths: Vec<usize> = lines.iter().map(|line| line.len()).collect();
let max_width = *widths.iter().max().unwrap_or(&0);
if max_width == 0 {
return 0;
}
// 使用位图表示角点以提高查找性能
let mut corner_bitmap = vec![vec![false; max_width]; height];
let mut grid: Vec<Vec<char>> = Vec::with_capacity(height);
for (row, line) in lines.iter().enumerate() {
let mut row_chars = Vec::with_capacity(line.len());
for (col, ch) in line.chars().enumerate() {
if ch == '+' {
corner_bitmap[row][col] = true;
}
row_chars.push(ch);
}
grid.push(row_chars);
}
let mut count = 0;
// 寻找矩形
for top in 0..height {
for left in 0..widths[top] {
if !corner_bitmap[top][left] {
continue;
}
for bottom in (top + 1)..height {
// 提前检查左下角
if left >= widths[bottom] || !corner_bitmap[bottom][left] {
continue;
}
for right in (left + 1)..widths[top].min(widths[bottom]) {
// 检查右上角和右下角
if !corner_bitmap[top][right] || !corner_bitmap[bottom][right] {
continue;
}
// 验证四条边
if is_valid_horizontal_line_optimized(&grid, top, left, right) &&
is_valid_horizontal_line_optimized(&grid, bottom, left, right) &&
is_valid_vertical_line_optimized(&grid, left, top, bottom) &&
is_valid_vertical_line_optimized(&grid, right, top, bottom) {
count += 1;
}
}
}
}
}
count
}
错误处理和边界情况
考虑更多边界情况的实现:
rust
#[derive(Debug, PartialEq)]
pub enum RectangleError {
EmptyInput,
InvalidCharacter,
}
impl std::fmt::Display for RectangleError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
RectangleError::EmptyInput => write!(f, "输入为空"),
RectangleError::InvalidCharacter => write!(f, "输入包含无效字符"),
}
}
}
impl std::error::Error for RectangleError {}
pub fn count(lines: &[&str]) -> u32 {
// 处理边界情况
if lines.is_empty() {
return 0;
}
let height = lines.len();
let widths: Vec<usize> = lines.iter().map(|line| line.len()).collect();
let max_width = *widths.iter().max().unwrap_or(&0);
if max_width == 0 {
return 0;
}
let mut corners = Vec::new();
let grid: Vec<Vec<char>> = lines
.iter()
.enumerate()
.map(|(row, line)| {
line.chars()
.enumerate()
.map(|(col, ch)| {
// 验证字符是否有效
if !matches!(ch, '+' | '-' | '|' | ' ') {
// 在实际应用中可能需要返回错误,但根据练习要求,我们忽略无效字符
}
if ch == '+' {
corners.push((row, col));
}
ch
})
.collect()
})
.collect();
let mut rectangle_count = 0;
for &(top, left) in &corners {
for &(bottom, right) in &corners {
if top >= bottom || left >= right {
continue;
}
if !corners.contains(&(top, right)) || !corners.contains(&(bottom, left)) {
continue;
}
if is_valid_horizontal_line(&grid, top, left, right) &&
is_valid_horizontal_line(&grid, bottom, left, right) &&
is_valid_vertical_line(&grid, left, top, bottom) &&
is_valid_vertical_line(&grid, right, top, bottom) {
rectangle_count += 1;
}
}
}
rectangle_count
}
// 返回Result的版本
pub fn count_safe(lines: &[&str]) -> Result<u32, RectangleError> {
if lines.is_empty() {
return Ok(0);
}
// 检查是否有无效字符
for line in lines {
for ch in line.chars() {
if !matches!(ch, '+' | '-' | '|' | ' ') {
return Err(RectangleError::InvalidCharacter);
}
}
}
Ok(count(lines))
}
// 支持自定义字符的版本
pub struct RectangleCounter {
corner_char: char,
horizontal_char: char,
vertical_char: char,
}
impl RectangleCounter {
pub fn new() -> Self {
RectangleCounter {
corner_char: '+',
horizontal_char: '-',
vertical_char: '|',
}
}
pub fn with_chars(corner: char, horizontal: char, vertical: char) -> Self {
RectangleCounter {
corner_char: corner,
horizontal_char: horizontal,
vertical_char: vertical,
}
}
pub fn count(&self, lines: &[&str]) -> u32 {
if lines.is_empty() {
return 0;
}
let height = lines.len();
let widths: Vec<usize> = lines.iter().map(|line| line.len()).collect();
let max_width = *widths.iter().max().unwrap_or(&0);
if max_width == 0 {
return 0;
}
let mut corners = Vec::new();
let grid: Vec<Vec<char>> = lines
.iter()
.enumerate()
.map(|(row, line)| {
line.chars()
.enumerate()
.map(|(col, ch)| {
if ch == self.corner_char {
corners.push((row, col));
}
ch
})
.collect()
})
.collect();
let mut rectangle_count = 0;
for &(top, left) in &corners {
for &(bottom, right) in &corners {
if top >= bottom || left >= right {
continue;
}
if !corners.contains(&(top, right)) || !corners.contains(&(bottom, left)) {
continue;
}
if self.is_valid_horizontal_line(&grid, top, left, right) &&
self.is_valid_horizontal_line(&grid, bottom, left, right) &&
self.is_valid_vertical_line(&grid, left, top, bottom) &&
self.is_valid_vertical_line(&grid, right, top, bottom) {
rectangle_count += 1;
}
}
}
rectangle_count
}
fn is_valid_horizontal_line(&self, grid: &[Vec<char>], row: usize, start_col: usize, end_col: usize) -> bool {
for col in start_col..=end_col {
match grid.get(row).and_then(|row_vec| row_vec.get(col)) {
Some(&ch) if ch == self.horizontal_char || ch == self.corner_char => continue,
_ => return false,
}
}
true
}
fn is_valid_vertical_line(&self, grid: &[Vec<char>], col: usize, start_row: usize, end_row: usize) -> bool {
for row in start_row..=end_row {
match grid.get(row).and_then(|row_vec| row_vec.get(col)) {
Some(&ch) if ch == self.vertical_char || ch == self.corner_char => continue,
_ => return false,
}
}
true
}
}
扩展功能
基于基础实现,我们可以添加更多功能:
rust
pub struct Rectangle {
pub top_left: (usize, usize),
pub top_right: (usize, usize),
pub bottom_left: (usize, usize),
pub bottom_right: (usize, usize),
pub width: usize,
pub height: usize,
}
pub struct RectangleAnalyzer {
lines: Vec<String>,
}
impl RectangleAnalyzer {
pub fn new(lines: &[&str]) -> Self {
RectangleAnalyzer {
lines: lines.iter().map(|s| s.to_string()).collect(),
}
}
pub fn count(&self) -> u32 {
count(&self.lines.iter().map(|s| s.as_str()).collect::<Vec<_>>())
}
pub fn find_all_rectangles(&self) -> Vec<Rectangle> {
let lines: Vec<&str> = self.lines.iter().map(|s| s.as_str()).collect();
if lines.is_empty() {
return vec![];
}
let height = lines.len();
let widths: Vec<usize> = lines.iter().map(|line| line.len()).collect();
let max_width = *widths.iter().max().unwrap_or(&0);
if max_width == 0 {
return vec![];
}
let mut corners = Vec::new();
let grid: Vec<Vec<char>> = lines
.iter()
.enumerate()
.map(|(row, line)| {
line.chars()
.enumerate()
.map(|(col, ch)| {
if ch == '+' {
corners.push((row, col));
}
ch
})
.collect()
})
.collect();
let mut rectangles = Vec::new();
for &(top, left) in &corners {
for &(bottom, right) in &corners {
if top >= bottom || left >= right {
continue;
}
if !corners.contains(&(top, right)) || !corners.contains(&(bottom, left)) {
continue;
}
if is_valid_horizontal_line(&grid, top, left, right) &&
is_valid_horizontal_line(&grid, bottom, left, right) &&
is_valid_vertical_line(&grid, left, top, bottom) &&
is_valid_vertical_line(&grid, right, top, bottom) {
rectangles.push(Rectangle {
top_left: (top, left),
top_right: (top, right),
bottom_left: (bottom, left),
bottom_right: (bottom, right),
width: right - left,
height: bottom - top,
});
}
}
}
rectangles
}
pub fn get_rectangle_at(&self, row: usize, col: usize) -> Option<Rectangle> {
let rectangles = self.find_all_rectangles();
rectangles.into_iter().find(|rect| {
row >= rect.top_left.0 && row <= rect.bottom_left.0 &&
col >= rect.top_left.1 && col <= rect.top_right.1
})
}
pub fn get_largest_rectangle(&self) -> Option<Rectangle> {
self.find_all_rectangles()
.into_iter()
.max_by_key(|rect| rect.width * rect.height)
}
pub fn get_smallest_rectangle(&self) -> Option<Rectangle> {
self.find_all_rectangles()
.into_iter()
.min_by_key(|rect| rect.width * rect.height)
}
pub fn count_by_size(&self) -> std::collections::HashMap<usize, usize> {
let rectangles = self.find_all_rectangles();
let mut size_counts = std::collections::HashMap::new();
for rect in rectangles {
let area = rect.width * rect.height;
*size_counts.entry(area).or_insert(0) += 1;
}
size_counts
}
}
fn is_valid_horizontal_line(grid: &[Vec<char>], row: usize, start_col: usize, end_col: usize) -> bool {
for col in start_col..=end_col {
match grid.get(row).and_then(|row_vec| row_vec.get(col)) {
Some(&('-' | '+')) => continue,
_ => return false,
}
}
true
}
fn is_valid_vertical_line(grid: &[Vec<char>], col: usize, start_row: usize, end_row: usize) -> bool {
for row in start_row..=end_row {
match grid.get(row).and_then(|row_vec| row_vec.get(col)) {
Some(&('|' | '+')) => continue,
_ => return false,
}
}
true
}
// 矩形可视化器
pub struct RectangleVisualizer;
impl RectangleVisualizer {
pub fn new() -> Self {
RectangleVisualizer
}
pub fn visualize_rectangles(lines: &[&str]) -> String {
let analyzer = RectangleAnalyzer::new(lines);
let rectangles = analyzer.find_all_rectangles();
if rectangles.is_empty() {
return lines.join("\n");
}
// 为每个矩形分配一个数字标记
let mut visualization = lines.to_vec().to_vec();
let mut grid: Vec<Vec<char>> = visualization
.iter()
.map(|line| line.chars().collect())
.collect();
for (i, rect) in rectangles.iter().enumerate() {
let marker = (i % 10).to_string().chars().next().unwrap();
// 标记四个角点
grid[rect.top_left.0][rect.top_left.1] = marker;
grid[rect.top_right.0][rect.top_right.1] = marker;
grid[rect.bottom_left.0][rect.bottom_left.1] = marker;
grid[rect.bottom_right.0][rect.bottom_right.1] = marker;
}
grid.iter()
.map(|row| row.iter().collect::<String>())
.collect::<Vec<String>>()
.join("\n")
}
}
// 便利函数
pub fn count(lines: &[&str]) -> u32 {
if lines.is_empty() {
return 0;
}
let height = lines.len();
let widths: Vec<usize> = lines.iter().map(|line| line.len()).collect();
let max_width = *widths.iter().max().unwrap_or(&0);
if max_width == 0 {
return 0;
}
let mut corners = Vec::new();
let grid: Vec<Vec<char>> = lines
.iter()
.enumerate()
.map(|(row, line)| {
line.chars()
.enumerate()
.map(|(col, ch)| {
if ch == '+' {
corners.push((row, col));
}
ch
})
.collect()
})
.collect();
let mut rectangle_count = 0;
for &(top, left) in &corners {
for &(bottom, right) in &corners {
if top >= bottom || left >= right {
continue;
}
if !corners.contains(&(top, right)) || !corners.contains(&(bottom, left)) {
continue;
}
if is_valid_horizontal_line(&grid, top, left, right) &&
is_valid_horizontal_line(&grid, bottom, left, right) &&
is_valid_vertical_line(&grid, left, top, bottom) &&
is_valid_vertical_line(&grid, right, top, bottom) {
rectangle_count += 1;
}
}
}
rectangle_count
}
pub fn analyze_rectangle_grid(lines: &[&str]) -> String {
let analyzer = RectangleAnalyzer::new(lines);
let count = analyzer.count();
let rectangles = analyzer.find_all_rectangles();
format!(
"网格包含 {} 个矩形\n\
所有矩形: {:?}\n\
最大矩形: {:?}\n\
最小矩形: {:?}",
count,
rectangles,
analyzer.get_largest_rectangle(),
analyzer.get_smallest_rectangle()
)
}
实际应用场景
矩形计数在实际开发中有以下应用:
- 计算机视觉:图像处理中的矩形物体检测
- 游戏开发:碰撞检测和游戏区域划分
- CAD软件:图形设计中的矩形识别
- 数据可视化:图表中的矩形元素识别
- OCR技术:光学字符识别中的文本框检测
- 网页解析:HTML页面布局分析
- 地图应用:地理信息系统中的区域识别
- 科学计算:网格计算和有限元分析
算法复杂度分析
-
时间复杂度:O(n⁴)
- 其中n是网格中角点的数量
- 需要检查所有可能的角点组合
-
空间复杂度:O(n)
- 需要存储角点位置和网格数据
与其他实现方式的比较
rust
// 使用递归的实现
pub fn count_recursive(lines: &[&str]) -> u32 {
fn count_helper(
grid: &[Vec<char>],
corners: &[(usize, usize)],
index: usize,
) -> u32 {
if index >= corners.len() {
return 0;
}
let (top, left) = corners[index];
let mut count = 0;
for &(bottom, right) in corners {
if top < bottom && left < right &&
corners.contains(&(top, right)) &&
corners.contains(&(bottom, left)) &&
is_valid_rectangle(grid, top, left, bottom, right) {
count += 1;
}
}
count + count_helper(grid, corners, index + 1)
}
// 实现细节省略
unimplemented!()
}
// 使用第三方库的实现
// [dependencies]
// image = "0.24"
// ndarray = "0.15"
use ndarray::Array2;
pub fn count_with_ndarray(lines: &[&str]) -> u32 {
// 使用ndarray处理二维数组
unimplemented!()
}
// 使用正则表达式的实现
use regex::Regex;
pub fn count_with_regex(lines: &[&str]) -> u32 {
// 使用正则表达式匹配矩形模式
unimplemented!()
}
// 使用并行计算的实现
// [dependencies]
// rayon = "1.5"
use rayon::prelude::*;
pub fn count_parallel(lines: &[&str]) -> u32 {
if lines.is_empty() {
return 0;
}
let grid: Vec<Vec<char>> = lines
.iter()
.map(|line| line.chars().collect())
.collect();
let corners: Vec<(usize, usize)> = (0..grid.len())
.flat_map(|row| {
(0..grid[row].len())
.filter_map(move |col| {
if grid[row][col] == '+' {
Some((row, col))
} else {
None
}
})
})
.collect();
corners
.par_iter()
.map(|&(top, left)| {
corners
.par_iter()
.filter(|&&(bottom, right)| {
top < bottom && left < right &&
corners.contains(&(top, right)) &&
corners.contains(&(bottom, left)) &&
is_valid_rectangle(&grid, top, left, bottom, right)
})
.count()
})
.sum::<usize>() as u32
}
fn is_valid_rectangle(
grid: &[Vec<char>],
top: usize,
left: usize,
bottom: usize,
right: usize,
) -> bool {
is_valid_horizontal_line(grid, top, left, right) &&
is_valid_horizontal_line(grid, bottom, left, right) &&
is_valid_vertical_line(grid, left, top, bottom) &&
is_valid_vertical_line(grid, right, top, bottom)
}
总结
通过 rectangles 练习,我们学到了:
- 二维网格处理:掌握了处理二维字符网格的技巧
- 几何算法:学会了实现几何图形识别算法
- 模式匹配:深入理解了字符模式匹配和验证
- 性能优化:学会了预处理数据和优化搜索算法
- 边界处理:理解了如何处理各种边界情况
- 扩展设计:学会了如何设计可扩展的系统架构
这些技能在实际开发中非常有用,特别是在计算机视觉、游戏开发、CAD软件、数据可视化等场景中。矩形计数虽然是一个具体的几何问题,但它涉及到了二维数组处理、模式匹配、几何算法、性能优化等许多核心概念,是学习Rust实用编程的良好起点。
通过这个练习,我们也看到了Rust在处理复杂几何计算和算法实现方面的强大能力,以及如何用安全且高效的方式实现经典的计算几何问题。这种结合了安全性和性能的语言特性正是Rust的魅力所在。