Last active
November 16, 2021 20:54
-
-
Save AnthonyMikh/f26608d3e3e75ff088c413dd793b548a to your computer and use it in GitHub Desktop.
Решение задачи №292 от UniLecs (Расшифровывать строку)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct Lexer<'a> { | |
s: &'a str, | |
} | |
type Num = usize; | |
#[derive(Debug)] | |
enum Lexeme<'a> { | |
String(&'a str), | |
Num(Num), | |
BracketOpen, | |
BracketClose, | |
} | |
impl<'a> Lexer<'a> { | |
fn of(s: &'a str) -> Self { | |
Self { s } | |
} | |
fn next(&mut self) -> Result<Option<Lexeme<'a>>, String> { | |
let mut chars = self.s.chars(); | |
let first = match chars.next() { | |
Some(ch) => ch, | |
None => return Ok(None), | |
}; | |
Ok(Some(match first { | |
'a'..='z' | 'A'..='Z' => { | |
let (str, rest) = peel_word(self.s); | |
self.s = rest; | |
Lexeme::String(str) | |
} | |
'0'..='9' => { | |
let (num, rest) = peel_num(self.s); | |
let num = num | |
.parse() | |
.map_err(|e| format!("failed to parse repetition number: {}", e))?; | |
self.s = rest; | |
Lexeme::Num(num) | |
} | |
'[' => { | |
self.s = chars.as_str(); | |
Lexeme::BracketOpen | |
} | |
']' => { | |
self.s = chars.as_str(); | |
Lexeme::BracketClose | |
} | |
other => return Err(format!("unexpected character {:?}", other)) | |
})) | |
} | |
} | |
fn split_off_which<P: Fn(char) -> bool>(s: &str, predicate: P) -> (&str, &str) { | |
s.split_at(s.find(|ch| !predicate(ch)).unwrap_or(s.len())) | |
} | |
fn peel_num(s: &str) -> (&str, &str) { | |
split_off_which(s, |ch| ch.is_ascii_digit()) | |
} | |
fn peel_word(s: &str) -> (&str, &str) { | |
split_off_which(s, |ch| ch.is_ascii_alphabetic()) | |
} | |
enum Encoded<'a> { | |
Literal(&'a str), | |
Repeated(Num, Vec<Self>), | |
} | |
impl Encoded<'_> { | |
fn append_to(&self, s: &mut String) { | |
match self { | |
Self::Literal(lit) => s.push_str(lit), | |
Self::Repeated(rep, inner) => { | |
for _ in 0..*rep { | |
for part in inner { | |
part.append_to(s); | |
} | |
} | |
} | |
} | |
} | |
} | |
impl<'a> Encoded<'a> { | |
fn parse(src: &'a str) -> Result<Vec<Self>, String> { | |
let mut stack = Vec::new(); | |
let mut current = Vec::new(); | |
let mut lexer = Lexer::of(src); | |
while let Some(lexeme) = lexer.next()? { | |
match lexeme { | |
Lexeme::String(s) => current.push(Encoded::Literal(s)), | |
Lexeme::Num(rep) => { | |
match lexer.next()? { | |
Some(Lexeme::BracketOpen) => (), // all is fine | |
other => return Err(format!("expected '[', got {:?}", other)), | |
} | |
stack.push((rep, current)); | |
current = Vec::new(); | |
} | |
Lexeme::BracketOpen => { | |
return Err("unexpected '[' without corresponding repetition number".into()) | |
} | |
Lexeme::BracketClose => { | |
let (rep, mut top) = stack.pop().ok_or_else(|| "unmatched ']'".to_string())?; | |
top.push(Encoded::Repeated(rep, current)); | |
current = top; | |
} | |
} | |
} | |
if !stack.is_empty() { | |
return Err("unmatched '['".into()); | |
} | |
Ok(current) | |
} | |
} | |
// The solution function | |
fn decode(s: &str) -> Result<String, String> { | |
let decoded = Encoded::parse(s)?; | |
let mut ret = String::new(); | |
for part in &decoded { | |
part.append_to(&mut ret); | |
} | |
Ok(ret) | |
} | |
// -------------------------- TESTS -------------------------- | |
#[test] | |
fn unilecs_examples() { | |
assert_eq!(decode("3[a]2[bc]").unwrap(), "aaabcbc"); | |
assert_eq!(decode("3[a2[c]]").unwrap(), "accaccacc"); | |
} | |
#[test] | |
fn deeply_nested() { | |
assert_eq!(decode("2[2[2[2[3[a]]]]]").unwrap(), "a".repeat(2 * 2 * 2 * 2 * 3)); | |
} | |
#[test] | |
fn rejects_invalid_input() { | |
macro_rules! assert_err { | |
($s:literal) => { | |
assert!(decode($s).is_err()); | |
} | |
} | |
assert_err!("["); | |
assert_err!("]"); | |
assert_err!("[]"); | |
assert_err!("2a[a]"); | |
assert_err!("!"); | |
assert_err!("10000000000000000000000[]"); | |
} | |
#[cfg(test)] | |
mod corner_cases { | |
use super::decode; | |
const EMPTY: String = String::new(); | |
#[test] | |
fn accepts_empty() { | |
assert_eq!(decode(""), Ok(EMPTY)); | |
} | |
#[test] | |
fn accepts_empty_repetition() { | |
assert_eq!(decode("3[]"), Ok(EMPTY)); | |
} | |
#[test] | |
fn accepts_one_as_repitition() { | |
assert_eq!(decode("1[]"), Ok(EMPTY)); | |
assert_eq!(decode("1[hey]"), Ok("hey".into())); | |
} | |
#[test] | |
fn accepts_zero_as_repitition() { | |
assert_eq!(decode("0[]"), Ok(EMPTY)); | |
assert_eq!(decode("0[fed3333[ffff]4[fff5[f]]]"), Ok(EMPTY)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Условия