Rust regex引擎如何处理重叠匹配?(AoC2023 Day1问题)
解决Advent of Code 2023 Day1第二部分的重叠数字匹配问题
你遇到的threeight这类重叠拼写数字的匹配问题,核心是标准非重叠正则匹配会漏掉重叠部分的第二个数字(比如只会匹配three,忽略后面的eight),而Rust的regex crate不支持环视语法,这里提供两种可行的解决思路:
方案1:遍历每个起始位置收集所有匹配
直接遍历字符串的每个索引,从每个位置尝试匹配数字正则,收集所有成功匹配的数字,最后取第一个和最后一个计算校准值。这种方式能捕获所有重叠的匹配项。
use regex::Regex; // 将匹配到的字符串转换为对应数字 fn str_to_digit(s: &str) -> Option<u32> { match s { "0" | "zero" => Some(0), "1" | "one" => Some(1), "2" | "two" => Some(2), "3" | "three" => Some(3), "4" | "four" => Some(4), "5" | "five" => Some(5), "6" | "six" => Some(6), "7" | "seven" => Some(7), "8" | "eight" => Some(8), "9" | "nine" => Some(9), _ => None, } } fn calculate_calibration(line: &str) -> u32 { let digit_re = Regex::new(r"[0-9]|zero|one|two|three|four|five|six|seven|eight|nine").unwrap(); let mut digits = Vec::new(); // 遍历每个字符位置,尝试匹配数字 for start in 0..line.len() { if let Some(matched) = digit_re.find(&line[start..]) { if let Some(digit) = str_to_digit(matched.as_str()) { digits.push(digit); } } } // 取第一个和最后一个数字计算结果 digits.first().unwrap() * 10 + digits.last().unwrap() }
方案2:反转字符串匹配最后一个数字
因为我们只需要第一个和最后一个数字,可以拆分处理:
- 第一个数字:用原正则从左到右匹配第一个结果
- 最后一个数字:将字符串反转,用反转后的数字正则匹配第一个结果,再转换回原数字
这种方法效率更高,无需遍历所有位置。
use regex::Regex; // 正向字符串转数字 fn str_to_digit(s: &str) -> u32 { match s { "0" | "zero" => 0, "1" | "one" => 1, "2" | "two" => 2, "3" | "three" => 3, "4" | "four" => 4, "5" | "five" => 5, "6" | "six" => 6, "7" | "seven" => 7, "8" | "eight" => 8, "9" | "nine" => 9, _ => unreachable!("Invalid digit string"), } } // 反转后的字符串转数字 fn reversed_str_to_digit(s: &str) -> u32 { match s { "0" | "orez" => 0, "1" | "eno" => 1, "2" | "owt" => 2, "3" | "eerht" => 3, "4" | "ruof" => 4, "5" | "evif" => 5, "6" | "xis" => 6, "7" | "neves" => 7, "8" | "thgie" => 8, "9" | "enin" => 9, _ => unreachable!("Invalid reversed digit string"), } } fn get_first_digit(line: &str) -> u32 { let re = Regex::new(r"[0-9]|zero|one|two|three|four|five|six|seven|eight|nine").unwrap(); str_to_digit(re.find(line).unwrap().as_str()) } fn get_last_digit(line: &str) -> u32 { let reversed_line: String = line.chars().rev().collect(); let re = Regex::new(r"[0-9]|orez|eno|owt|eerht|ruof|evif|xis|neves|thgie|enin").unwrap(); reversed_str_to_digit(re.find(&reversed_line).unwrap().as_str()) } fn calculate_calibration(line: &str) -> u32 { get_first_digit(line) * 10 + get_last_digit(line) }
内容的提问来源于stack exchange,提问作者HyperCodec
相关产品推荐
相关产品推荐

