Executando verificação de segurança...
4

[AJUDA]Como fazer o codigo descobrir palavras para você?

Pessoal então, queria ideias de como resolver esse problema, não consegui pensar em uma solução muito pratica pra ele.

Ent o problema é o seguinte, tenho uma lista de palavras chaves:

{
[house, secret, gun, kill...]
}

ent a ideia é essa lista ser realmente grande.

Ent imagine que o programa aceita um input do usuário do tipo string, como faria para descobrir se ele digitou a palavra mesmo tentando máscarar ela, exemplo:
"ak8di3lc7l"
->"aK8dI3Lc7L"
ou
->"dki7hjl5fhjl"
ou podemos ir mais além.
(Essa é só pela curiosidade)
E se o usuário quiser passar a informação de uma forma mais complexa, como de trás para frente:
->"68sl6hslgsji68hsk"
ou sla a ordem não importar, mas sim se bater com as mesmas letras do array.

Bem sou iniciante neste mundo da programação, talvez esse seja um problema simples, mas realmente travei aqui, não consigo pensar em algo realmente prático pra isso, a ideia é fazer algo realmente eficiente sem um monte de foreif.

Poderia pegar somente as letras que aparecem e tentando combinalas e ver se consigo 1 palavra?
Só de pensar nisso já assusta, mas sinto que o caminha tá por aqui mesmo.

Eai?

Carregando publicação patrocinada...
7

Podemos dividir o algoritmo em dois casos: se os caracteres devem estar na mesma ordem da palavra (ou na ordem inversa), ou se podem estar embaralhados. Cada um dos casos leva a um algoritmo diferente.

De qualquer forma, primeiro concentre-se em fazer o algoritmo para uma palavra, depois vc consegue aplicá-lo para todas as palavras da lista.


Caracteres em ordem

Suponha que a palavra seja "house". Então vc pega a string que o usuário digitou e procura pela primeira posição em que ocorre o "h".

Se tiver "h", vc procura pela próxima letra (no caso, "o"), mas faz esta busca a partir da posição em que o "h" foi encontrado.

Se encontrar, vai pra próxima letra (no caso, "u"), buscando-a a partir da posição em que a letra "o" foi encontrada. E assim por diante.

Se chegar no final da string antes de encontrar o último caractere da palavra, é porque ela não ocorre. Se encontrou a última letra da palavra, é porque ocorre.

Em JavaScript seria algo assim:

// procura pela palavra dentro da string "str"
function procura(palavra, str) {
    // se a palavra é maior que a string, com certeza não ocorre
    if (palavra.length > str.length) {
        return false;
    }

    // converte ambas para minúsculo
    palavra = palavra.toLowerCase();
    str = str.toLowerCase();

    var posicaoAtual = -1; // posição a partir da qual será feita a busca na string
    for (var i = 0; i < palavra.length; i++) {
        // Verifica se o caractere da palavra (na posição "i") ocorre na string
        // Busca a partir da última posição em que um caractere foi encontrado
        posicaoAtual = str.indexOf(palavra[i], posicaoAtual + 1);
        if (posicaoAtual === -1) { // não encontrou, então não ocorre
            return false;
        }
    }
    // se chegou aqui, é porque encontrou todos os caracteres da palavra
    return true;
}

console.log(procura('kill', 'aK8dI3Lc7L')); // true
console.log(procura('kill', 'dki7hjl5fhjl')); // true
console.log(procura('kill', 'dki7hjl5')); // false

// assim, tanto faz se os caracteres são letras ou não, funciona do mesmo jeito
console.log(procura('123', 'abc_1  2xxxx3.')); // true

Repare que desta forma, tanto faz se os caracteres envolvidos são letras ou não. Claro que se quiser, pode fazer uma validação extra para verificar se a string só tem letras, mas pelo que entendi, não existe esta restrição.


Agora, para a lista, basta usar a função acima em um loop:

// procura pela palavra dentro da string "str"
function procura(palavra, str) {
    // se a palavra é maior que a string, com certeza não ocorre
    if (palavra.length > str.length) {
        return false;
    }

    // converte ambas para minúsculo
    palavra = palavra.toLowerCase();
    str = str.toLowerCase();

    var posicaoAtual = -1; // posição a partir da qual será feita a busca na string
    for (var i = 0; i < palavra.length; i++) {
        // Verifica se o caractere da palavra (na posição "i") ocorre na string
        // Busca a partir da última posição em que um caractere foi encontrado
        posicaoAtual = str.indexOf(palavra[i], posicaoAtual + 1);
        if (posicaoAtual === -1) { // não encontrou, então não ocorre
            return false;
        }
    }
    // se chegou aqui, é porque encontrou
    return true;
}

// inverte uma string
function reverse(s) {
    return s.split('').reverse().join('');
}

// verifica se alguma das palavras ocorre na string "str" ou no inverso dela
function procuraLista(palavras, str) {
    var inverso = reverse(str);
    for (var palavra of palavras) {
        if (procura(palavra, str)) {
            console.log(`"${palavra}" encontrada`);
            return; // se encontrou, pode sair
        } else if (procura(palavra, inverso)) {
            console.log(`"${palavra}" encontrada de trás pra frente`);
            return; // se encontrou, pode sair
        }
    }
    // se chegou aqui, é porque percorreu todas as palavras e não encontrou nenhuma
    console.log('Nenhuma das palavras foi encontrada');
}

var palavras = ['house', 'secret', 'gun', 'kill'];

procuraLista(palavras, 'aK8dI3Lc7L'); // "kill" encontrada
procuraLista(palavras, '68sl6hslgsji68hsk'); // "kill" encontrada de trás pra frente

procuraLista(palavras, '123 G_xyzU...nabc!'); // "gun" encontrada
procuraLista(palavras, '123 nxU__Gabc!'); // "gun" encontrada de trás pra frente

procuraLista(palavras, 'abcdefghi'); // Nenhuma das palavras foi encontrada

Claro que a função procuraLista, em vez de imprimir a mensagem, poderia retornar o resultado - por exemplo, algo como { palavraEncontrada: 'kill', invertido: false } ou algo do tipo (e null caso não encontre nada). Mas aí fica a seu critério, pois o algoritmo básico está aí.


Caracteres em qualquer ordem

Neste caso, não precisa gerar todas as permutações possíveis, pois além de desnecessário, é uma solução que não escala. Por exemplo, se a string tiver apenas 10 caracteres, serão mais de 3 milhões de permutações para testar. Com 20 caracteres, são mais de 2 quintilhões de possibilidades. Não precisa de nada disso.

Basta que vc verifique quais caracteres estão na palavra, e quantas vezes cada um ocorre. Depois, basta comparar esta contagem com as ocorrências na string.

Por exemplo, "kill" tem uma letra "k", uma letra "i" e duas letras "l". Basta que a string tenha no mínimo estas quantidades de cada uma destas letras, para que vc considere a ocorrência.

Então a ideia é primeiro construir um objeto Map com a contagem de todos os caracteres da palavra.

Depois, para cada caractere da string, basta ver se ele está na contagem (e se estiver, subtraio 1). Se a contagem zerar, é porque todos os caracteres foram encontrados na quantidade em que ocorrem na palavra.

Seria algo assim:

// procura pela palavra dentro da string "str"
// mas os caracteres não precisam estar na mesma ordem
function procuraQualquerOrdem(palavra, str) {
    // se a palavra é maior que a string, com certeza não ocorre
    if (palavra.length > str.length) {
        return false;
    }

    // obtém a contagem dos caracteres da palavra
    var contPalavra = new Map();
    for (var char of palavra.toLowerCase()) {
        var n = contPalavra.get(char) ?? 0;
        contPalavra.set(char, n + 1);
    }

    // para cada caractere da string, verifica se ele está na contagem da palavra
    for (var char of str.toLowerCase()) {
        // se o caractere está na contagem, diminui 1
        if (contPalavra.has(char)) {
            var n = contPalavra.get(char);
            if (n == 1) { // se ocorre apenas uma vez, pode remover
                contPalavra.delete(char);
                if (contPalavra.size == 0) {
                    // se a contagem esvaziou, é porque todos os caracteres foram encontrados
                    return true;
                }
            } else { // senão, diminui 1 ocorrência
                contPalavra.set(char, n - 1);
            }
        }
    }
    // se chegou aqui, é porque a contagem não esvaziou (ou seja, faltou caracteres, então a palavra não ocorre)
    return false;
}

// verifica se alguma das palavras ocorre na string "str"
function procuraLista(palavras, str) {
    for (var palavra of palavras) {
        if (procuraQualquerOrdem(palavra, str)) {
            console.log(`"${palavra}" encontrada`);
            return;
        }
    }
    console.log('Nenhuma das palavras foi encontrada');
}

var palavras = ['house', 'secret', 'gun', 'kill'];

// testando os casos anteriores (letras na ordem)
procuraLista(palavras, 'aK8dI3Lc7L'); // "kill" encontrada
procuraLista(palavras, '68sl6hslgsji68hsk'); // "kill" encontrada

procuraLista(palavras, '123 G_xyzU...nabc!'); // "gun" encontrada
procuraLista(palavras, '123 nxU__Gabc!'); // "gun" encontrada

procuraLista(palavras, 'abcdefghi'); // Nenhuma das palavras foi encontrada

// testando letras fora de ordem
procuraLista(palavras, 'xL_Kalxi123'); // "kill" encontrada
procuraLista(palavras, 'o1haxs95465Uabce_la'); // "house" encontrada

Porém, veja que agora não tem como saber se os caracteres estavam invertidos ou não, pois agora estou verificando-os em qualquer ordem. Por isso, não preciso mais inverter a string e fazer outra busca.


Outro detalhe é que, se uma palavra for encontrada, a função procuraLista para de procurar as demais.

Mas se quiser que ele procure por todas, bastaria mudar a função para retornar um array com todas as palavras encontradas:

function procuraLista(palavras, str) {
    var encontradas = [];
    for (var palavra of palavras) {
        if (procuraQualquerOrdem(palavra, str)) {
            encontradas.push(palavra);
        }
    }
    return encontradas;
}

var palavras = ['house', 'secret', 'gun', 'kill'];

console.log(procuraLista(palavras, 'lkhogulisne')); // [ 'house', 'gun', 'kill' ]
console.log(procuraLista(palavras, 'housecret')); // [ 'house', 'secret' ]

Ou ainda:

function procuraLista(palavras, str) {
    return palavras.filter(palavra => procuraQualquerOrdem(palavra, str));
}

Mas agora surgiu outro caso que deve ser pensado o que fazer. Repare que "housecret" encontrou tanto "house" quando "secret", afinal cada busca é feita de forma isolada. Pode-se até argumentar que se "house" já foi encontrada, então a busca deveria considerar apenas os caracteres restantes (no caso, "cret") e aí o algoritmo ficaria mais complexo (precisaria remover os caracteres das palavras já encontradas). Mas acho que estou indo além, pois a ideia básica está aí pra vc aprimorar.

2

Talvez guardando as palavras da lista e suas inversões dentro de uma estrutura de dados chamada TRIE (nao é TREE rsrs) você consiga alcançar a solução do seu problema ate com um tempo de busca interessante, caso funcione.

Tente e poste o codigo da solução aqui pra vermos.

-2

Então, meu mano, eu faria assim:

  1. Pegaria a entrada do usuário e removeria números e outros caracteres que não fossem letras, e deixaria todas as letras minúsculas.
  2. Criaria uma função que gera uma permutação e verifica se essa nova palavra combina com alguma palavra do array.

Dá uma pesquisada sobre permutações, é um tema interessante e bate com o que você está tentando criar.