diff --git a/Retos/Reto #36 - PERMUTACIONES [Media]/rust/hdescobarh.rs b/Retos/Reto #36 - PERMUTACIONES [Media]/rust/hdescobarh.rs index d77910a868..ca929d649f 100644 --- a/Retos/Reto #36 - PERMUTACIONES [Media]/rust/hdescobarh.rs +++ b/Retos/Reto #36 - PERMUTACIONES [Media]/rust/hdescobarh.rs @@ -1,61 +1,70 @@ // author: hdescobarh (Hans D. Escobar H.) // Este modulo tiene mi solución -use crate::permutacion_naive::RamaArbol; +use crate::permutacion_naive::reordenar_texto; fn main() { /* Implementación naive O(n!): - case_sensitive - - acepta cualquier caracter Unicode (e.g., el texto '漢字한국어' es valido). + - acepta cualquier carácter Unicode (e.g., el texto '漢字한국어' es valido). Reordenar la secuencia es equivalente a, dada una secuencia de tamaño n, generar un arbol de n! hojas, - con (n-k) ramificaciones desde cada nodo en cada nivel, siendo la raíz del arbol el nivel 0 + con (n-k) ramificaciones desde cada nodo en el nivel k (0<=k string, None => continue, }; + let para_imprimir: Vec = reordenar_texto(texto); + println!("{:?}", para_imprimir); + } +} + +mod permutacion_naive { + /* + * El algoritmo sigue una estructura de arbol en la que cada nodo, representado con el struct RamaArbol, + * tiene una permutación de tamaño 0 <= t =< longitud_palabra (camino_secuencia) y una colección + * de los longitud_palabra - t caracteres aún no incluidos (restantes). + * + * Ejemplo: el texto "abcd" la raíz comienza con un camino de 4 espacios vacíos y un restantes de 4; + * En el siguiente nivel genera 4 nodos (uno por carácter restante), cada uno permutaciones de + * tamaño 1 y un restantes de 3: + * - "a" y ['b', 'c', 'd'] + * - "b" y ['a', 'c', 'd'] + * - "c" y ['a', 'b', 'f'] + * - "d" y ['a', 'b', 'c'] + */ + + pub fn reordenar_texto(texto: String) -> Vec { // Inicializa el nivel 0 let tree_root = RamaArbol::desde_string(texto); - // caché para la memoización. Se inicia con capacidad n! para reducir el allocation - let mut cache: Vec = Vec::with_capacity(factorizar(tree_root.restantes.len())); - + let mut cache: Vec = + Vec::with_capacity(util_factorial(tree_root.restantes.len())); // El método para generar todas las hojas del arbol es recursivo RamaArbol::generar_hojas(&mut cache, vec![tree_root]); - // Finalmente, extrae las secuencias de la caché para imprimirlas - let para_imprimir: Vec = cache + cache .into_iter() - .map(|rama| rama.camino_sequencia) - .collect(); - - println!("{:?}", para_imprimir); + .map(|rama| rama.camino_secuencia) + .collect() } -} -mod permutacion_naive { - - /** - * Cada nodo tiene una secuencia de caracteres hasta ese nodo y los nodos que faltan por usar. - * Por ejemplo, para el texto "abcd" la raíz comienza con un camino de 4 espacios vacio y un restantes de 4; - * Uno de los nodos del siguiente nivel tendra una string con un valor y 3 vacios, y un vector - * de restantes de tamaño 3. Por ejemplo "a" y ['b', 'c', 'd'] - */ #[derive(Clone)] - pub struct RamaArbol { - pub camino_sequencia: String, - pub restantes: Vec, + struct RamaArbol { + camino_secuencia: String, + restantes: Vec, } impl RamaArbol { pub fn desde_string(s: String) -> Self { RamaArbol { - camino_sequencia: String::with_capacity(s.len()), + camino_secuencia: String::with_capacity(s.len()), restantes: s.chars().collect(), } } @@ -72,14 +81,14 @@ mod permutacion_naive { /*Hace una deep copy de la Rama, remueve un caracter en el indice de los restantes y lo agrega al final de la secuencia */ let mut rama: RamaArbol = ramas.clone(); - rama.camino_sequencia + rama.camino_secuencia .push(rama.restantes.swap_remove(indice)); nuevas_ramas.push(rama); } nuevas_ramas } - pub fn generar_hojas(resultado: &mut Vec, base: Vec) { + fn generar_hojas(resultado: &mut Vec, base: Vec) { for rama in base { if rama.restantes.is_empty() { resultado.push(rama); @@ -90,14 +99,14 @@ mod permutacion_naive { } } } -} -fn factorizar(numero: usize) -> usize { - let mut resultado = 1; - for valores in 1..(numero + 1) { - resultado *= valores; + fn util_factorial(numero: usize) -> usize { + let mut resultado = 1; + for valores in 1..(numero + 1) { + resultado *= valores; + } + resultado } - resultado } use std::io; @@ -123,7 +132,7 @@ pub fn entrada_de_texto() -> Option { }; if input.chars().count() > 11 { - println!("La longitud es muy grande la el algoritmo implementado.\n"); + println!("La longitud es muy grande para el algoritmo implementado.\n"); None } else { Some(input.trim().to_string()) @@ -132,55 +141,94 @@ pub fn entrada_de_texto() -> Option { // Unit Tests #[cfg(test)] -mod tests { - use crate::factorizar; - use crate::permutacion_naive::RamaArbol; +mod test { + use super::*; + use std::collections::HashSet; #[test] - fn texto_vacio() { - let texto = String::from(""); - let tree_root = RamaArbol::desde_string(texto); - let mut cache: Vec = Vec::with_capacity(factorizar(tree_root.restantes.len())); - RamaArbol::generar_hojas(&mut cache, vec![tree_root]); - assert_eq!("".to_string(), cache[0].camino_sequencia); + fn manejo_texto_vacio_correcto() { + let resultado: HashSet = reordenar_texto(String::from("")).into_iter().collect(); + let esperado = HashSet::from(["".to_string()]); + assert!(esperado == resultado); } #[test] - fn texto_2() { - let texto = String::from("ab"); - let tree_root = RamaArbol::desde_string(texto); - let mut cache: Vec = Vec::with_capacity(factorizar(tree_root.restantes.len())); - RamaArbol::generar_hojas(&mut cache, vec![tree_root]); - let mut resultado: Vec = cache - .into_iter() - .map(|rama| rama.camino_sequencia) - .collect(); - resultado.sort(); - let esperado = ["ab", "ba"]; - for index in 0..1 { - assert_eq!(esperado[index], resultado[index]) - } + fn reordena_texto_minimo() { + let resultado: HashSet = reordenar_texto(String::from("ab")).into_iter().collect(); + let esperado: HashSet = ["ab", "ba"].into_iter().map(|s| s.to_string()).collect(); + assert!(esperado == resultado); } #[test] - fn texto_4() { - let texto = String::from("hLOa"); - let tree_root = RamaArbol::desde_string(texto); - let mut cache: Vec = Vec::with_capacity(factorizar(tree_root.restantes.len())); - RamaArbol::generar_hojas(&mut cache, vec![tree_root]); - let mut resultado: Vec = cache - .into_iter() - .map(|rama| rama.camino_sequencia) - .collect(); - resultado.sort(); - let mut esperado = [ + fn reordena_texto_con_mayusculas() { + let resultado: HashSet = + reordenar_texto(String::from("hLOa")).into_iter().collect(); + let esperado: HashSet = [ "hOLa", "OhLa", "LhOa", "hLOa", "OLha", "LOha", "LOah", "OLah", "aLOh", "LaOh", "OaLh", "aOLh", "ahLO", "haLO", "LahO", "aLhO", "hLaO", "LhaO", "OhaL", "hOaL", "aOhL", "OahL", "haOL", "ahOL", - ]; - esperado.sort(); - for index in 0..24 { - assert_eq!(esperado[index], resultado[index]) - } + ] + .into_iter() + .map(|s| s.to_string()) + .collect(); + assert!(esperado == resultado); + } + + #[test] + fn reordena_palabra_longitud_seis() { + let resultado: HashSet = reordenar_texto(String::from("celula")) + .into_iter() + .collect(); + let esperado: HashSet = [ + "celula", "eclula", "lceula", "cleula", "elcula", "lecula", "leucla", "elucla", + "ulecla", "luecla", "eulcla", "uelcla", "uclela", "culela", "lucela", "ulcela", + "cluela", "lcuela", "eculla", "ceulla", "ueclla", "euclla", "cuella", "ucella", + "lcelua", "clelua", "elclua", "leclua", "cellua", "ecllua", "llecua", "ellcua", + "lelcua", "clleua", "lcleua", "llceua", "lulcea", "ullcea", "llucea", "luclea", + "ulclea", "clulea", "lculea", "ucllea", "cullea", "clluea", "lcluea", "llcuea", + "elulca", "leulca", "uellca", "eullca", "luelca", "ulelca", "ulleca", "luleca", + "llueca", "leluca", "elluca", "lleuca", "leluac", "elluac", "lleuac", "leulac", + "elulac", "ulelac", "luelac", "eullac", "uellac", "ulleac", "luleac", "llueac", + "aleluc", "laeluc", "ealluc", "aelluc", "lealuc", "elaluc", "ellauc", "lelauc", + "lleauc", "laleuc", "alleuc", "llaeuc", "ualelc", "aulelc", "luaelc", "ulaelc", + "aluelc", "lauelc", "laeulc", "aleulc", "elaulc", "leaulc", "aelulc", "ealulc", + "eulalc", "uelalc", "leualc", "elualc", "ulealc", "luealc", "auellc", "uaellc", + "eaullc", "aeullc", "ueallc", "euallc", "lualec", "ulalec", "alulec", "laulec", + "uallec", "aullec", "llauec", "alluec", "laluec", "ullaec", "lulaec", "lluaec", + "aelclu", "ealclu", "laeclu", "aleclu", "elaclu", "leaclu", "lecalu", "elcalu", + "clealu", "lcealu", "eclalu", "celalu", "calelu", "aclelu", "lcaelu", "claelu", + "alcelu", "lacelu", "eacllu", "aecllu", "ceallu", "ecallu", "acellu", "caellu", + "laelcu", "alelcu", "elalcu", "lealcu", "aellcu", "eallcu", "lleacu", "ellacu", + "lelacu", "allecu", "lalecu", "llaecu", "lclaeu", "cllaeu", "llcaeu", "lcaleu", + "claleu", "alcleu", "lacleu", "calleu", "aclleu", "allceu", "lalceu", "llaceu", + "elclau", "leclau", "cellau", "ecllau", "lcelau", "clelau", "clleau", "lcleau", + "llceau", "lelcau", "ellcau", "llecau", "leucal", "elucal", "ulecal", "luecal", + "eulcal", "uelcal", "ueclal", "euclal", "cuelal", "ucelal", "eculal", "ceulal", + "clueal", "lcueal", "ucleal", "culeal", "luceal", "ulceal", "elcual", "lecual", + "celual", "eclual", "lceual", "cleual", "aleucl", "laeucl", "ealucl", "aelucl", + "leaucl", "elaucl", "eluacl", "leuacl", "uelacl", "eulacl", "lueacl", "uleacl", + "uaelcl", "auelcl", "eualcl", "uealcl", "aeulcl", "eaulcl", "lauecl", "aluecl", + "ulaecl", "luaecl", "aulecl", "ualecl", "caleul", "acleul", "lcaeul", "claeul", + "alceul", "laceul", "laecul", "alecul", "elacul", "leacul", "aelcul", "ealcul", + "eclaul", "celaul", "lecaul", "elcaul", "cleaul", "lceaul", "acelul", "caelul", + "eaclul", "aeclul", "cealul", "ecalul", "ucalel", "cualel", "auclel", "uaclel", + "caulel", "aculel", "acluel", "caluel", "lacuel", "alcuel", "clauel", "lcauel", + "luacel", "ulacel", "alucel", "laucel", "ualcel", "aulcel", "culael", "uclael", + "lcuael", "cluael", "ulcael", "lucael", "eucall", "uecall", "ceuall", "ecuall", + "uceall", "cueall", "cuaell", "ucaell", "acuell", "cauell", "uacell", "aucell", + "aecull", "eacull", "caeull", "aceull", "ecaull", "ceaull", "ueacll", "euacll", + "auecll", "uaecll", "eaucll", "aeucll", "alucle", "laucle", "ualcle", "aulcle", + "luacle", "ulacle", "ulcale", "lucale", "culale", "uclale", "lcuale", "cluale", + "caulle", "aculle", "ucalle", "cualle", "auclle", "uaclle", "lacule", "alcule", + "claule", "lcaule", "aclule", "calule", "laluce", "alluce", "llauce", "laulce", + "alulce", "ulalce", "lualce", "aullce", "uallce", "ullace", "lulace", "lluace", + "clalue", "lcalue", "acllue", "callue", "laclue", "alclue", "allcue", "lalcue", + "llacue", "lclaue", "cllaue", "llcaue", "luclae", "ulclae", "clulae", "lculae", + "ucllae", "cullae", "llcuae", "clluae", "lcluae", "ullcae", "lulcae", "llucae", + ] + .into_iter() + .map(|s| s.to_string()) + .collect(); + assert!(esperado == resultado); } }