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()