Olá, Gian! Tudo bem?
No caso, não é efetuada a busca quando temos três letras e apagamos uma por causa da condição do filter, que só aceita um input com três ou mais letras. Quando apagamos a terceira letra, o valor é inválido, e não é salvo pelo distinctUntilChanged, sendo assim, no caso de ALU, em que apagamos o U, distinctUntilChanged continua guardando o valor ALU. Quando digitamos U novamente, ele compara os dois e confirma que são iguais, sendo assim a requisição não é feita.
Quando temos mais letras, sempre estamos passando os valores para o distinctUntilChanged, e, por exemplo, salvamos ALUR4, se apagarmos o 4, o valor ALUR é passado para o distinctUntilChanged, que identifica como um valor diferente do que tinha salvo antes (ALUR4), portanto faz a nova requisição e guarda o valor ALUR como o valor atual.
Ao colocarmos o 4 novamente, o funcionamento não é igual ao cenário com apenas três caracteres porque o valor, para o distinctUntilChanged mudou, e ele refaz a operação.
Talvez a melhor forma de garantir que uma requisição a mais não seja feita caso o usuário apague e tecle novamente rapidamente, é usar do debounceTime. Então, se o usuário acidentalmente apaga a letra e rapidamente coloca de volta, o debounceTime pode garantir que esse erro não acabe em uma requisição.
Entretanto, usar um mecanismo para evitar que uma requisição seja feita quando o usuário apaga e reescreve algo, que não seja via limite mínimo de caracteres e debounceTime é prejudicial para o próprio usuário, que talvez queira voltar, ver todos os itens com ALUR e continuar com ALUR4, por exemplo.
Espero ter ajudado!