Rust: codewars的Simple Substitution Cipher Helper算法题、N种不简单的解法集

来源:互联网 发布:指定dns解析域名 编辑:程序博客网 时间:2024/06/01 08:57

codewars是一个不错的刷题的网站,特别是对于rust爱好者而者,资源稀缺,是打发时光的利器。

今天不是专门安利codewars而来,主要是谈一道简单的加密和解密算法题。
说明:来源于codewars,Simple Substitution Cipher Helper。

一、要求如下:

其实就是26个字母,打乱后,但一一对应。要求对任一字符串,能进行加密和解密。

比如

let map1 = "abcdefghijklmnopqrstuvwxyz";let map2 = "etaoinshrdlucmfwypvbgkjqxz";let cipher = Cipher::new(map1, map2);cipher.encode("abc") // => "eta"cipher.encode("xyz") // => "qxz"cipher.encode("aeiou") // => "eirfg"cipher.decode("eta") // => "abc"cipher.decode("qxz") // => "xyz"cipher.decode("eirfg") // => "aeiou"

如果超出26个字母以外的字符,不加密。即”好”=》“好”

二、N种不同的算法,大开眼界

只能说,牛人太多,爽爽爽!这个是最好的教材。除本法提交的算法外,在此选集了近10种codewars中的精彩的解法,供平时学习。

1、个人提交的算法

use std::collections::HashMap;struct Cipher {    encode: HashMap<String, String>,    decode: HashMap<String, String>,}impl Cipher {    fn new(map1: &str, map2: &str) -> Cipher {        let _encode = map1.chars()            .map(|x| x.to_string())            .zip(map2.chars().map(|y| y.to_string()))            .collect::<HashMap<String, String>>();        let _decode = map2.chars()            .map(|x| x.to_string())            .zip(map1.chars().map(|y| y.to_string()))            .collect::<HashMap<String, String>>();        Cipher {            encode: _encode,            decode: _decode,        }    }    fn encode(&self, string: &str) -> String {        let mut _strs = String::from("");        let strs: Vec<_> = string.chars()            .map(|x| match self.encode.get(&x.to_string()) {                Some(s) => _strs.push_str(s),                _ => _strs.push_str(&x.to_string()),            })            .collect();        _strs    }    fn decode(&self, string: &str) -> String {        let mut _strs = String::from("");        let strs: Vec<_> = string.chars()            .map(|x| match self.decode.get(&x.to_string()) {                Some(s) => _strs.push_str(s),                _ => _strs.push_str(&x.to_string()),            })            .collect();        _strs    }}

2、codewars的最佳算法

struct Cipher {  map: Vec<(char, char)>}impl Cipher {  fn new(map1: &str, map2: &str) -> Cipher {    Cipher {      map: map1.chars().zip(map2.chars()).collect()    }  }  fn encode(&self, string: &str) -> String {    string.chars().map(|c| self.map.iter().find(|x| x.0 == c).map_or(c, |y| y.1)).collect()  }  fn decode(&self, string: &str) -> String {    string.chars().map(|c| self.map.iter().find(|x| x.1 == c).map_or(c, |y| y.0)).collect()  }}

3、其它算法

use std::collections::HashMap;struct Cipher {  encode_map: HashMap<char, char>,  decode_map: HashMap<char, char>,}impl Cipher {  fn new(map1: &str, map2: &str) -> Cipher {    let mut encode_map = HashMap::new();    encode_map.extend(map1.chars().zip(map2.chars()));    let mut decode_map = HashMap::new();    decode_map.extend(map2.chars().zip(map1.chars()));    Cipher { encode_map: encode_map, decode_map: decode_map }  }  fn encode(&self, string: &str) -> String {    string.chars().map(|c| self.encode_map.get(&c).map_or(c, |v| *v)).collect()  }  fn decode(&self, string: &str) -> String {    string.chars().map(|c| self.decode_map.get(&c).map_or(c, |v| *v)).collect()  }}

4、BTreeMap

use std::collections::BTreeMap;struct Cipher {  // We need a bidirectional cipher implementation, with dictionary per direction.  // BTreeMap should likely work faster than HashMap with simplest hasher in our case.  etree: BTreeMap<char, char>,  dtree: BTreeMap<char, char>,}impl Cipher {  fn new(map1: &str, map2: &str) -> Cipher {    Cipher {      // Hopefully BTreeMap is created balanced.      etree: map1.chars().zip(map2.chars()).collect(),      dtree: map2.chars().zip(map1.chars()).collect(),    }  }  // Here be dragons, krakens, sandworms and impaired (de-)referencing conventions.  fn encode(&self, string: &str) -> String {    string.chars().map(|c| self.etree.get(&c).cloned().unwrap_or(c)).collect()  }  fn decode(&self, string: &str) -> String {    string.chars().map(|c| self.dtree.get(&c).cloned().unwrap_or(c)).collect()  }}

5、String

struct Cipher {  map1: String,  map2: String,}impl Cipher {  fn new(map1: &str, map2: &str) -> Cipher {    Cipher { map1: map1.into(), map2: map2.into()}  }  fn encode(&self, string: &str) -> String {    string.chars().map(    |c| {      match self.map1.find(c) {        Some(i) => self.map2.chars().nth(i).unwrap(),        _ => c      }    }    ).collect()  }  fn decode(&self, string: &str) -> String {    string.chars().map(    |c| {      match self.map2.find(c) {        Some(i) => self.map1.chars().nth(i).unwrap(),        _ => c      }    }    ).collect()  }}

6、Vec

struct Cipher {  from: Vec<u8>,  to: Vec<u8>,}impl Cipher {  fn new(map1: &str, map2: &str) -> Cipher {    Cipher {      from: map1.as_bytes().to_vec(),      to: map2.as_bytes().to_vec(),    }  }  fn encode(&self, string: &str) -> String {    string.chars()      .map(|c| self.from.iter().position(|&f| f == c as u8).map_or(c, |p| self.to[p] as char))      .collect()  }  fn decode(&self, string: &str) -> String {    string.chars()      .map(|c| self.to.iter().position(|&f| f == c as u8).map_or(c, |p| self.from[p] as char))      .collect()  }}

7、

use std::collections::HashMap;fn get_default(m: &HashMap<char,char>,k: &char) -> char {    match m.get(k) {        Some(val) => *val,        None => *k    }}struct Cipher {  encoding: HashMap<char,char>,  decoding: HashMap<char,char>}impl Cipher {  fn new(map1: &str, map2: &str) -> Cipher {        let mut encoding = HashMap::new();        let mut decoding = HashMap::new();        for (x,y) in map1.chars().zip(map2.chars()) {            encoding.insert(x,y);            decoding.insert(y,x);        }        Cipher{encoding:encoding,decoding:decoding}  }  fn encode(&self, string: &str) -> String {    string.chars().map(|c| get_default(&self.encoding,&c).to_string())      .collect::<Vec<String>>().join("")  }  fn decode(&self, string: &str) -> String {    string.chars().map(|c| get_default(&self.decoding,&c).to_string())      .collect::<Vec<String>>().join("")  }}

8、

use std::collections::HashMap;struct Cipher {  encode: HashMap<char, char>,  decode: HashMap<char, char>,}fn _code(map: &HashMap<char, char>, string: &str) -> String {    string.chars()      .map(|x| map.get(&x).unwrap_or(&x).clone())      .collect()}impl Cipher {  fn new(map1: &str, map2: &str) -> Cipher {    Cipher {      encode: map1.chars()                .zip(map2.chars())                .collect(),      decode: map2.chars()                .zip(map1.chars())                .collect(),    }  }  fn encode(&self, string: &str) -> String {    _code(&self.encode, string)  }  fn decode(&self, string: &str) -> String {    _code(&self.decode, string)  }}

9、

struct Cipher<'a> {    map1:&'a [u8],    map2:&'a [u8]}impl<'a> Cipher<'a> {    fn new(map1: &'a str, map2: &'a str) -> Cipher<'a> {        Cipher{map1:map1.as_bytes(), map2:map2.as_bytes()}    }    fn encode(&self, string: &str) -> String {        string.bytes()            .map(|b|                 self.map1                    .iter()                    .position(|&m| b==m)                    .and_then(|i| self.map2.get(i))                    .cloned()                    .unwrap_or(b) as char            )            .collect()    }    fn decode(&self, string: &str) -> String {        self.decoder().encode(string)    }    fn decoder(&self) -> Cipher{        Cipher{map1:self.map2, map2:self.map1}    }}

10、

struct Cipher {  table : std::collections::HashMap<char,char>}impl Cipher {  fn new(map1: &str, map2: &str) -> Cipher {    Cipher {      table : map1.chars().zip(map2.chars()).collect()    }  }  fn encode(&self, string: &str) -> String {    string.chars().map(|x| self.table.get(&x).cloned().unwrap_or(x)).collect()  }  fn decode(&self, string: &str) -> String {    string.chars().map(|x| self.decode_char(x)).collect()  }  fn decode_char(&self, c: char) -> char {    self.table.iter().find(|kv| *kv.1 == c).map(|kv| *kv.0).unwrap_or(c)  }}

11、个人认为的最佳解法

use std::collections::HashMap;struct Cipher {  encode: HashMap<char, char>,  decode: HashMap<char, char>}impl Cipher {  fn new(map1: &str, map2: &str) -> Self {    let encode = map1.chars().zip(map2.chars()).collect();    let decode = map2.chars().zip(map1.chars()).collect();    Cipher{encode: encode, decode: decode}  }  fn encode(&self, string: &str) -> String {    string.chars().map(|ref c| *self.encode.get(c).unwrap_or(c)).collect()  }  fn decode(&self, string: &str) -> String {    string.chars().map(|ref c| *self.decode.get(c).unwrap_or(c)).collect()  }}

三、个别点评

看了其它人10种解法外,个人认为第11种解法最佳,思路清晰,代码简单,最为优雅,第10种也差不多,当然,各种优势。

我的解法的思路虽然相同,但用String并不是一个好的选择,过于笨重,增加了不少的代码量。

1、用char代替String

//在上面场景中,HashMap<char, char>较HashMap<String, String>结构更轻巧。fn new(map1: &str, map2: &str) {    let _encode = map1.chars()        .zip(map2.chars())        .collect::<HashMap<char, char>>();}//没有过多的转化

2、使用unwrap_or,可以省了一大段的代码:

    string.chars().map(|ref c| *self.encode.get(c).unwrap_or(c)).collect()