Executando verificação de segurança...
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.

Carregando publicação patrocinada...