简体   繁体   中英

How can I write a Rust function to find different characters between two strings?

The order of the characters is not important but the count is. I mean aaabaaa equals to 6a + b and the function is like math subtraction. For example:

fn diff(a: String, b: String) -> String {}
diff("aabbac", "accba") => "ab"
---------------------------------
"aabbac" = (3a+2b+c)
"accba" = (2a+b+2c)
(3a+2b+c) - (2a+b+2c) = a+b // -c is ignored

The usual technique is to create a function that counts the number of occurrences of each char, like collections.Counter in Python , and to compare these numbers for strings a and b .

The Rust standard library documentation contains a snippet that does the job. This is an adaptation that accepts any iterator:

use std::collections::HashMap;
use std::hash::Hash;
use std::iter::Iterator;

fn counter<T, I>(it: I) -> HashMap<T, usize>
where
    T: Eq + Hash,
    I: Iterator<Item = T>,
{
    let mut count_by_element = HashMap::new();
    for e in it {
        *count_by_element.entry(e).or_insert(0) += 1;
    }
    count_by_element
}

Now that we know how to build a map char -> count , we just have to compare the counts of the string a and b :

use std::iter;

fn diff(a: &str, b: &str) -> String {
    let mut v: Vec<char> = vec![];
    let counter_a = counter(a.chars());
    let counter_b = counter(b.chars());
    for (c, n_a) in &counter_a {
        let n_b = counter_b.get(c).unwrap_or(&0); // how many `c` in `b`?
        if n_a > n_b {
            v.extend(iter::repeat(c).take(n_a - n_b)); // add `n_a - n_b` `c`s
        }
    }
    v.into_iter().collect::<String>() // build the String
}

If you want a "one shot" function, you can forget the counter function and use a more direct approach:

fn diff_one_shot(a: &str, b: &str) -> String {
    let mut counter = HashMap::new();
    for c in a.chars() {
        *counter.entry(c).or_insert(0) += 1; // one more
    }
    for c in b.chars() {
        *counter.entry(c).or_insert(0) -= 1; // one less
    }
    counter
        .iter()
        .filter(|(_c, &n)| n > 0) // only if more `c` in `a` than in `b`
        .flat_map(|(c, &n)| iter::repeat(c).take(n)) // `n` times `c`
        .collect::<String>()
}

Examples:

fn main() {
    println!("{:?}", counter("aaabbc".chars()));
    // {'b': 2, 'c': 1, 'a': 3}
    println!("{}", diff("aaabbc", "ab"));
    //aabc
    println!("{}", diff_one_shot("aaabbc", "ab"));
    //aacb
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM