Skip to content

Instantly share code, notes, and snippets.

@AnthonyMikh
Last active November 16, 2021 20:54
Show Gist options
  • Save AnthonyMikh/f26608d3e3e75ff088c413dd793b548a to your computer and use it in GitHub Desktop.
Save AnthonyMikh/f26608d3e3e75ff088c413dd793b548a to your computer and use it in GitHub Desktop.
Решение задачи №292 от UniLecs (Расшифровывать строку)
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));
}
}
@AnthonyMikh
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment