Rust 练习册 96:Rectangles与几何计算

在计算机图形学和几何计算中,识别和计数矩形是一个经典问题。在 Exercism 的 "rectangles" 练习中,我们需要在一个由 ASCII 字符组成的网格中识别并计数所有可能的矩形。这不仅能帮助我们掌握二维数组处理和几何算法,还能深入学习Rust中的迭代器、模式匹配和复杂逻辑处理。

什么是矩形计数问题?

矩形计数问题要求我们在一个由特定字符组成的网格中找出所有由 '+'、'-' 和 '|' 字符构成的矩形。一个有效的矩形必须满足以下条件:

  1. 四个角由 '+' 字符表示
  2. 水平边由 '-' 字符组成
  3. 垂直边由 '|' 字符组成
  4. 矩形的边必须是连续的

例如,以下是一个包含两个矩形的网格:

复制代码
  +-+
  | |
+-+-+
| |  
+-+  

让我们先看看练习提供的函数签名:

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. 核心要求

  1. 网格解析:正确解析由字符串切片组成的二维网格
  2. 角点识别:识别所有可能的矩形角点('+' 字符)
  3. 矩形验证:验证由四个角点组成的图形是否为有效矩形
  4. 计数统计:统计所有有效矩形的数量

2. 技术要点

  1. 二维坐标系统:理解和操作二维网格坐标
  2. 字符匹配:识别和验证特定字符
  3. 几何算法:实现矩形识别算法
  4. 性能优化:优化搜索和验证过程

完整实现

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()
    )
}

实际应用场景

矩形计数在实际开发中有以下应用:

  1. 计算机视觉:图像处理中的矩形物体检测
  2. 游戏开发:碰撞检测和游戏区域划分
  3. CAD软件:图形设计中的矩形识别
  4. 数据可视化:图表中的矩形元素识别
  5. OCR技术:光学字符识别中的文本框检测
  6. 网页解析:HTML页面布局分析
  7. 地图应用:地理信息系统中的区域识别
  8. 科学计算:网格计算和有限元分析

算法复杂度分析

  1. 时间复杂度:O(n⁴)

    • 其中n是网格中角点的数量
    • 需要检查所有可能的角点组合
  2. 空间复杂度: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 练习,我们学到了:

  1. 二维网格处理:掌握了处理二维字符网格的技巧
  2. 几何算法:学会了实现几何图形识别算法
  3. 模式匹配:深入理解了字符模式匹配和验证
  4. 性能优化:学会了预处理数据和优化搜索算法
  5. 边界处理:理解了如何处理各种边界情况
  6. 扩展设计:学会了如何设计可扩展的系统架构

这些技能在实际开发中非常有用,特别是在计算机视觉、游戏开发、CAD软件、数据可视化等场景中。矩形计数虽然是一个具体的几何问题,但它涉及到了二维数组处理、模式匹配、几何算法、性能优化等许多核心概念,是学习Rust实用编程的良好起点。

通过这个练习,我们也看到了Rust在处理复杂几何计算和算法实现方面的强大能力,以及如何用安全且高效的方式实现经典的计算几何问题。这种结合了安全性和性能的语言特性正是Rust的魅力所在。

相关推荐
JienDa41 分钟前
JienDa聊PHP:电商实战中主流PHP框架的协同策略与架构优化
开发语言·架构·php
切糕师学AI42 分钟前
Spring 是什么?
java·后端·spring
星释44 分钟前
Rust 练习册 97:Run-Length Encoding 压缩算法
java·linux·rust
老神在在0011 小时前
Mybatis01
后端·学习·spring·java-ee·mybatis
Tongfront1 小时前
前端通用submit方法
开发语言·前端·javascript·react
c***72741 小时前
SpringBoot + vue 管理系统
vue.js·spring boot·后端
JienDa1 小时前
JienDa聊PHP:起卦、卜卦平台实战中PHP框架的协同架构方略
开发语言·架构·php
Le1Yu1 小时前
订单优化(状态机、分库分表、覆盖索引、缓存优化查询)
java·开发语言·数据库
s***35301 小时前
SpringBoot集成Prometheus
spring boot·后端·prometheus