¿Cómo solucionar un problema de ordenamiento de palabras alienígena en JavaScript?
¡Vamos a sumergirnos en el fascinante mundo del desarrollo de software! Resolveremos un problema práctico de ordenamiento de palabras en un lenguaje "alienígena" utilizando JavaScript. Aprender a descomponer un problema en partes pequeñas y manejables es vital para cualquier desarrollador.
¿Cómo crear un mapa de diccionario alienígena?
El primer paso en nuestra solución es construir un "mapa de diccionario" que nos ayude a entender el orden de las letras en este idioma alienígena. Esto lo haremos con una estructura de datos hash en JavaScript:
// Crear mapa de diccionario alienígenalet mapaDiccionario =newMap();for(let i =0; i < orden.length; i++){ mapaDiccionario.set(orden[i], i);}
Este hash table almacena cada letra del idioma alienígena como clave y su respectivo orden como valor, permitiéndonos acceder rápidamente al índice de cualquier letra.
¿Cómo comparar palabras en base al diccionario creado?
Una vez tenemos nuestro mapa, enfrentamos el siguiente desafío: comparar palabras para verificar si están ordenadas correctamente. La clave aquí es iterar sobre las palabras y sus caracteres:
functioncomparar(palabra1, palabra2){// Determinar longitud mínima para comparaciónlet minLength =Math.min(palabra1.length, palabra2.length);for(let i =0; i < minLength; i++){let char1 = mapaDiccionario.get(palabra1[i]);let char2 = mapaDiccionario.get(palabra2[i]);if(char1 !== char2){return char1 < char2;}}return palabra1.length<= palabra2.length;}
Esta función auxiliar evalúa letra por letra hasta encontrar una diferencia, determinando si una palabra debe preceder a otra según el orden alienígena.
¿Cuál es la mecánica para verificar el orden de todas las palabras?
Finalmente, dado nuestro mapa y función de comparación, verificamos si una lista entera de palabras está ordenada:
functionisAlienSorted(palabras){for(let i =1; i < palabras.length; i++){if(!comparar(palabras[i -1], palabras[i])){returnfalse;}}returntrue;}
Aquí, iteramos sobre las palabras comparando consecutivamente pares de palabras. Si alguna comparación falla, determinamos que el orden no es correcto.
¿Cómo evaluar la eficiencia de nuestra solución?
Al abordar problemas algorítmicos es crucial considerar la eficiencia. Este algoritmo tiene complejidad O(N * M), donde N es la cantidad de palabras y M es el máximo de caracteres en una palabra. Afortunadamente, el procesamiento de cadenas dentro de las capacidades actuales de hardware hace que tal complejidad sea manejable.
Utilizar un enfoque modular y claro no solo simplifica el mantenimiento del código sino que también facilita comprender y depurar algoritmos más complejos en el futuro. ¡Sigue desarrollando tus habilidades y recuerda que cada ejercicio es una oportunidad para mejorar!
#include<iostream>#include<unordered_map>#include<vector>usingnamespace std;unordered_map<char,int> order;boolcompare_words(string word1, string word2){int n =min(word1.size(), word2.size());for(int i =0; i < n;++i){if(order[word1[i]]< order[word2[i]])returntrue;if(order[word1[i]]> order[word2[i]])returnfalse;}return word1.size()<= word2.size();}boolare_words_sorted(vector<string> words, string alphabet){for(int i =0; i < alphabet.size();++i) order[alphabet[i]]= i;for(int i =1; i < words.size();++i){if(!compare_words(words[i -1], words[i]))returnfalse;}returntrue;}intmain(){ vector<string> words ={"habito","lectura","sonreir"}; string alien_alphabet ="hlabcdefgijkmnopqrstuvwxyz"; cout <<"Is Sorted : "<<are_words_sorted(words, alien_alphabet)<<"\n"; order.clear(); vector<string> words2 ={"conocer","cono"}; string alien_alphabet2 ="abcdefhgijklmnopqrstuvwxyz"; cout <<"Is Sorted : "<<are_words_sorted(words2, alien_alphabet2)<<"\n";}
Aquí mi solución usando Java:
public boolean isAlienSorted(String[] words,String order){Map<Character,Integer> dict =newHashMap<Character,Integer>();for(int index =0; index <26; index++){ dict.put(order.charAt(index), index +1);}for(int index =0; index +1< words.length; index++){if(words[index].equals(words[index +1])){continue;} int wordLength = words[index].length()> words[index +1].length()? words[index].length(): words[index +1].length();for(int wordIndex =0; wordIndex < wordLength; wordIndex++){ int val1 = words[index].length()> wordIndex ? dict.get(words[index].charAt(wordIndex)):0; int val2 = words[index +1].length()> wordIndex ? dict.get(words[index +1].charAt(wordIndex)):0;if(val1 > val2){returnfalse;}elseif(val1 < val2){break;}}}returntrue;}
No conocia el metodo charAt( ), esta muy genial tu solución, yo lo que hacia para obtener los caracteres de un texto era construir un char [ ] con la función toCharArray( ) de la siguiente manera:
🧐Si necesitas un poco de contexto de JavaScript...
🌎Variables globales en JavaScript
Si una variable se define sin las palabras reservadas var, let o const, se creará en el scope global, lo que significa que estará disponible en todo el código, incluso fuera de la función o bloque donde se creó. Esto permite el acceso de la variable mapa_diccionario en la función comparar. En Python, por ejemplo, se puede lograr esto con la palabra reservada global.
.
Si no quieres este comportamiento, puedes declararla con let o const y redefinir los parámetros de la función comparar, ya que la necesita.
Si defines un valor a una propiedad con la notación corchetes, estas no estarán bajo el control de la estructura Map. Por ejemplo, los métodos has o delete no funcionarán, o no se podrá iterar sus valores con un for ... of.
.
Es como si se hubiera definido como un objeto.
const mapa_diccionario ={}
Por lo que es mejor definir una propiedad con su valor con el método set y obtener el valor con el método get. Así se aprovechará mejor esta estructura.
.
También puedes definir el mapa_diccionario con el constructor de Map, utilizando como argumento una lista de listas, donde cada sublista tiene la forma [key, value]. La complejidad seguirá siendo constante.
Me causo un poco de confusión la complejidad de este algoritmo, así que me puse analizarlo:
aquí la complejidad en tiempo es O(n*m) porque en la primera iteración se hace a través de las palabras y la segunda iteración es a través de los caracteres de cada palabra la cual cada palabra no tiene la misma longitud que el arreglo de palabras (al menos no en todos los casos), si no fuera n*n o n^2 .
Y la complejidad en espacio en este caso es O(1) , es contraintuitivo porque podriamos pensar que al estar usando un arreglo lineal como un diccionario para almacenar el alfabeto entonces el crecimiento es lineal de acuerdo al tamaña de la entrada o el alfabeto (orden), pero en este caso el tamaño de orden o del alfabeto puede ser fijo o tiene un limite, en este caso son los 26 caracteres del alfabeto ingles ordenados lexiograficamente a un idioma alienigena; entonces como la complejidad en espacio depende del tamaño de entrada y el alfabeto no va mas alla de 26 caracteres, la complejidad es constante.
Antes de ver el video dejare esto aqui :v
functionisAlienSorted(palabras, orden){let isOrder =true;for(let i =0; i < palabras.length; i++){const i2 = i +1;if(i2 < palabras.length){const palabraA = palabras[i]const palabraB = palabras[i2]let maxL;if(palabraA.length> palabraB.length){ maxL = palabraA.length;}else{ maxL = palabraB.length;}for(let h =0; h < maxL; h++){if(palabraA[h]!= palabraB[h]){const indexA = orden.indexOf(palabraA[h]);const indexB = orden.indexOf(palabraB[h])if(indexA > indexB){ isOrder =false;}break;}}if(!isOrder){break;}}}return isOrder
}
Solución propuesta en C#
Aunque no atiende el escenario en el que no existe un caracter en el alfabeto recibido. --
public bool isAlienSorted(List<string> listOfWords, string orderedAlphabet){//create hash table with the purpose of reducing algorithm complexity as hash table is O(1)Hashtable alphabet =newHashtable();for(int i =0; i < orderedAlphabet.Length; i++){ alphabet.Add(orderedAlphabet[i],i);}// loop on list of words to go one by one comparing first with secondfor(int i =0; i < listOfWords.Count-1; i++){ string evalWord1 = listOfWords[i]; string evalWord2 = listOfWords[i+1];Console.WriteLine($"Evaluating: {evalWord1} and {evalWord2} ");for(int l =0; l <Math.Min(evalWord1.Length, evalWord2.Length); l++){Console.WriteLine($"Comparing letter {l}: {evalWord1[l]} -> {evalWord2[l]}");if((int)alphabet[evalWord1[l]]>(int)alphabet[evalWord2[l]])//TODO: add validation for missing letters or try..catch to handle it{returnfalse;}}if(evalWord1.Length> evalWord2.Length){returnfalse;}}returntrue;}
Mi solución en go
package main
func isAlienSorted(palabras []string, orden string) bool {dictMap:=make(map[rune]int)var isOrdered bool
for i,value:= range orden { dictMap[value]= i +1}fori:=0; i <len(palabras)-1; i++{ isOrdered =comparar(palabras[i], palabras[i+1], dictMap)}return isOrdered
}func comparar(palabra1 string, palabra2 string, dictionary map[rune]int) bool {var longitud int
iflen(palabra1)<len(palabra2){ longitud =len(palabra1)}else{ longitud =len(palabra2)}fori:=0; i < longitud; i++{ actual_char,siguiente_char:= palabra1[i], palabra2[i]if actual_char == siguiente_char {if i == longitud-1&&len(palabra1)>len(palabra2){returnfalse}}elseif dictionary[rune(actual_char)]> dictionary[rune(siguiente_char)]{returnfalse}elseif dictionary[rune(actual_char)]< dictionary[rune(siguiente_char)]{returntrue}}}
Mi solución en pythondef solucion(pals, orden): di = {} for i in range(len(orden)): di[orden[i]] = i for i in range(len(pals)-1): a = min(len(pals[i]), len(pals[i+1])) for j in range(a): if (di[pals[i][j]] > di[pals[i+1][j]]): return False elif (di[pals[i][j]] < di[pals[i+1][j]]): break return True
print(solucion(words1, order1))
def solucion(pals, orden): di ={}for i inrange(len(orden)): di[orden[i]]= i
for i inrange(len(pals)-1): a =min(len(pals[i]),len(pals[i+1]))for j inrange(a):if(di[pals[i][j]]> di[pals[i+1][j]]):returnFalseelif(di[pals[i][j]]< di[pals[i+1][j]]):breakreturnTrueprint(solucion(words1, order1))
#include<iostream>;#include<vector>;#include<string>;bool in_order(const int order[],conststd::vector<std::string>&words){ int p1 =0; int p2 =1; int pair_flag =0; int c =0;while(p1<words.size()-1&& p2 < words.size()){ pair_flag =0; c =0;while(!pair_flag && c < words[p1].size()&& c < words[p2].size()){if(order[words[p1][c]-'a']< order[words[p2][c]-'a']){ pair_flag =1;}elseif(order[words[p1][c]-'a']== order[words[p2][c]-'a']){ c++;}else{returnfalse;}}if(!pair_flag && words[p1].size()> words[p2].size()){returnfalse;} p1++; p2++;}returntrue;}int main(){ int count =0; int order_map[26];//std::string alphabet = "abcdefghijklmnopqrstuvwxyz";std::string alphabet ="hlabcdefgijkmnopqrstuvwxyz";for(auto c: alphabet){ order_map[c -'a']= count; count++;}//std::vector<std::string> words = {"conocer", "cono"};std::vector<std::string> words ={"habito","hacer","sonreir","lectura"};if(in_order(order_map, words)){std::cout <<"awesome"<< std::endl;}else{std::cout <<"not so awesome"<< std::endl;}return0;}
Solucion en Python mas corta y precisa
def is_alien_sorted(words, order): dict_map ={}for i, char inenumerate(order): dict_map[char]= i
for i inrange(len(words)-1): word1 = words[i] word2 = words[i +1]for j inrange(min(len(word1),len(word2))):if word1[j]!= word2[j]:if dict_map[word1[j]]> dict_map[word2[j]]:returnFalsebreakelse:iflen(word1)>len(word2):returnFalsereturnTrue
Dejo mi solución en JavaScript antes de ver la clase: