141 lines
4.2 KiB
Rust
141 lines
4.2 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
|
pub struct Word(Vec<CharStatus<String>>);
|
|
|
|
impl Word {
|
|
pub fn chars(&self) -> Vec<CharStatus<String>> {
|
|
self.0.clone()
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
|
pub enum CharStatus<T> {
|
|
NotContained(T),
|
|
Contained(T),
|
|
Match(T),
|
|
Unknown,
|
|
}
|
|
|
|
pub(super) fn compare_strings(s1: &str, s2: &str) -> Word {
|
|
let mut result: Vec<CharStatus<String>> = Vec::with_capacity(s1.len());
|
|
result.resize_with(s1.len(), || CharStatus::Unknown);
|
|
|
|
let mut s1_char_count: HashMap<char, usize> = HashMap::new();
|
|
let mut s2_char_count: HashMap<char, usize> = HashMap::new();
|
|
|
|
for c in s1.chars() {
|
|
*s1_char_count.entry(c).or_insert(0) += 1;
|
|
}
|
|
|
|
for ((c1, c2), res) in s1.chars().zip(s2.chars()).zip(result.iter_mut()) {
|
|
if c1 == c2 {
|
|
*res = CharStatus::Match(c2.to_string());
|
|
*s2_char_count.entry(c2).or_insert(0) += 1;
|
|
} else {
|
|
*res = CharStatus::Unknown;
|
|
}
|
|
}
|
|
|
|
for (res, c2) in result.iter_mut().zip(s2.chars()) {
|
|
if res == &CharStatus::Unknown {
|
|
let c1_count = s1_char_count.get(&c2).unwrap_or(&0);
|
|
let c2_count = s2_char_count.get(&c2).unwrap_or(&0);
|
|
|
|
if *c1_count > 0 && c1_count > c2_count {
|
|
*res = CharStatus::Contained(c2.to_string());
|
|
*s2_char_count.entry(c2).or_insert(0) += 1;
|
|
} else {
|
|
*res = CharStatus::NotContained(c2.to_string());
|
|
}
|
|
}
|
|
}
|
|
|
|
Word(result)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
|
|
use super::*;
|
|
#[test]
|
|
fn test_compare_strings() {
|
|
let source = "HALLO";
|
|
|
|
let want = Word(vec![
|
|
CharStatus::NotContained("0".to_owned()),
|
|
CharStatus::NotContained("0".to_owned()),
|
|
CharStatus::NotContained("0".to_owned()),
|
|
CharStatus::NotContained("0".to_owned()),
|
|
CharStatus::NotContained("0".to_owned()),
|
|
]);
|
|
let input = "00000";
|
|
|
|
let got = compare_strings(source, input);
|
|
assert_eq!(want, got);
|
|
let source = "HALLO";
|
|
|
|
let want = Word(vec![
|
|
CharStatus::NotContained("L".to_owned()),
|
|
CharStatus::NotContained("L".to_owned()),
|
|
CharStatus::Match("L".to_owned()),
|
|
CharStatus::Match("L".to_owned()),
|
|
CharStatus::NotContained("L".to_owned()),
|
|
]);
|
|
let input = "LLLLL";
|
|
|
|
let got = compare_strings(source, input);
|
|
assert_eq!(want, got);
|
|
|
|
let want = Word(vec![
|
|
CharStatus::Match("H".to_owned()),
|
|
CharStatus::Match("A".to_owned()),
|
|
CharStatus::Match("L".to_owned()),
|
|
CharStatus::Match("L".to_owned()),
|
|
CharStatus::Match("O".to_owned()),
|
|
]);
|
|
let input = "HALLO";
|
|
|
|
let got = compare_strings(source, input);
|
|
assert_eq!(want, got);
|
|
|
|
let want = Word(vec![
|
|
CharStatus::Match("H".to_owned()),
|
|
CharStatus::NotContained("L".to_owned()),
|
|
CharStatus::Match("L".to_owned()),
|
|
CharStatus::Match("L".to_owned()),
|
|
CharStatus::Match("O".to_owned()),
|
|
]);
|
|
let input = "HLLLO";
|
|
|
|
let got = compare_strings(source, input);
|
|
assert_eq!(want, got);
|
|
|
|
let want = Word(vec![
|
|
CharStatus::Match("H".to_owned()),
|
|
CharStatus::Contained("L".to_owned()),
|
|
CharStatus::Match("L".to_owned()),
|
|
CharStatus::NotContained("I".to_owned()),
|
|
CharStatus::NotContained("L".to_owned()),
|
|
]);
|
|
let input = "HLLIL";
|
|
|
|
let got = compare_strings(source, input);
|
|
assert_eq!(want, got);
|
|
|
|
let want = Word(vec![
|
|
CharStatus::Contained("L".to_owned()),
|
|
CharStatus::NotContained("L".to_owned()),
|
|
CharStatus::Match("L".to_owned()),
|
|
CharStatus::Contained("A".to_owned()),
|
|
CharStatus::Match("O".to_owned()),
|
|
]);
|
|
let input = "LLLAO";
|
|
|
|
let got = compare_strings(source, input);
|
|
assert_eq!(want, got);
|
|
}
|
|
}
|