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:
function procura(palavra, str) {
if (palavra.length > str.length) {
return false;
}
palavra = palavra.toLowerCase();
str = str.toLowerCase();
var posicaoAtual = -1;
for (var i = 0; i < palavra.length; i++) {
posicaoAtual = str.indexOf(palavra[i], posicaoAtual + 1);
if (posicaoAtual === -1) {
return false;
}
}
return true;
}
console.log(procura('kill', 'aK8dI3Lc7L'));
console.log(procura('kill', 'dki7hjl5fhjl'));
console.log(procura('kill', 'dki7hjl5'));
console.log(procura('123', 'abc_1 2xxxx3.'));
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:
function procura(palavra, str) {
if (palavra.length > str.length) {
return false;
}
palavra = palavra.toLowerCase();
str = str.toLowerCase();
var posicaoAtual = -1;
for (var i = 0; i < palavra.length; i++) {
posicaoAtual = str.indexOf(palavra[i], posicaoAtual + 1);
if (posicaoAtual === -1) {
return false;
}
}
return true;
}
function reverse(s) {
return s.split('').reverse().join('');
}
function procuraLista(palavras, str) {
var inverso = reverse(str);
for (var palavra of palavras) {
if (procura(palavra, str)) {
console.log(`"${palavra}" encontrada`);
return;
} else if (procura(palavra, inverso)) {
console.log(`"${palavra}" encontrada de trás pra frente`);
return;
}
}
console.log('Nenhuma das palavras foi encontrada');
}
var palavras = ['house', 'secret', 'gun', 'kill'];
procuraLista(palavras, 'aK8dI3Lc7L');
procuraLista(palavras, '68sl6hslgsji68hsk');
procuraLista(palavras, '123 G_xyzU...nabc!');
procuraLista(palavras, '123 nxU__Gabc!');
procuraLista(palavras, 'abcdefghi');
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:
function procuraQualquerOrdem(palavra, str) {
if (palavra.length > str.length) {
return false;
}
var contPalavra = new Map();
for (var char of palavra.toLowerCase()) {
var n = contPalavra.get(char) ?? 0;
contPalavra.set(char, n + 1);
}
for (var char of str.toLowerCase()) {
if (contPalavra.has(char)) {
var n = contPalavra.get(char);
if (n == 1) {
contPalavra.delete(char);
if (contPalavra.size == 0) {
return true;
}
} else {
contPalavra.set(char, n - 1);
}
}
}
return false;
}
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'];
procuraLista(palavras, 'aK8dI3Lc7L');
procuraLista(palavras, '68sl6hslgsji68hsk');
procuraLista(palavras, '123 G_xyzU...nabc!');
procuraLista(palavras, '123 nxU__Gabc!');
procuraLista(palavras, 'abcdefghi');
procuraLista(palavras, 'xL_Kalxi123');
procuraLista(palavras, 'o1haxs95465Uabce_la');
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'));
console.log(procuraLista(palavras, 'housecret'));
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.