Isomorphic Strings
Duas strings são isomórficas quando você pode renomear os caracteres da primeira para obter a segunda. Todas as ocorrências de um caractere recebem o mesmo novo nome, dois caracteres diferentes nunca recebem o mesmo nome, e um caractere pode manter o próprio nome. Para duas strings s e t do mesmo comprimento, retorne true quando forem isomórficas e false quando não forem.
Função
- sstring
- a string a ser renomeada, letras e dígitos
- tstring
- uma string a ser alcançada, com o mesmo comprimento que s
- Retornaboolean
- verdadeiro se uma renomeação bijetiva dos caracteres de s resulta em t
Restrições
1 ≤ s.length ≤ 2 × 104t.length == s.lengthsetcontêm apenas letras do alfabeto inglês e dígitos. Letras maiúsculas e minúsculas são caracteres diferentes.
Exemplos
- Entrada
- s = "noon"t = "deed"
- Saída
- true
- Explicação
- Renomeie
nparadeoparae. Todas as posições estão de acordo com essa renomeação, e as duas letras recebem nomes diferentes.
- Entrada
- s = "moon"t = "deed"
- Saída
- false
- Explicação
- Da esquerda para a direita,
mse tornadeose tornae, e entãontambém teria que se tornard. Duas letras diferentes não podem compartilhar um nome, então a resposta éfalse.
- Entrada
- s = "r2d2"t = "c3p3"
- Saída
- true
- Explicação
rse tornac,2se torna3edse tornap. Dígitos também são caracteres, e ambos os2se tornam3.
Para ir além
Dada uma lista de palavras, você consegue agrupar as que são isomorfas entre si sem comparar todos os pares de palavras?
Dicas
Abra uma de cada vez. Cada uma revela um pouco mais.
Renomeie os caracteres de
suma posição de cada vez, da esquerda para a direita. Quando uma renomeação feita anteriormente atrapalha?Registre em qual caractere de
tcada caractere desse transformou. Esse registro não detecta um erro: dois caracteres diferentes desse transformarem no mesmo caractere det. Um segundo registro, detde volta paras, detecta esse erro.Percorra as duas strings juntas com dois mapas. Se nem
s[i]nemt[i]estiverem mapeados ainda, mapeie-os um para o outro. Caso contrário, eles já devem apontar um para o outro; se não apontarem, retornefalse.
Solução
Uma renomeação deve funcionar nos dois sentidos: cada caractere de s se transforma em um caractere de t, e cada caractere de t vem de um caractere de s. Dois mapeamentos, um para cada direção, verificam ambos em uma única passagem.
Compare cada par de posições
Correta, mas não termina nos maiores testes
Intuição
Uma renomeação mantém iguais os caracteres iguais e diferentes os caracteres diferentes. Portanto, para cada par de posições i e j, s[i] == s[j] deve produzir a mesma resposta que t[i] == t[j]. Isso também é suficiente: se todos os pares concordarem, renomear cada caractere de s para o caractere de t em suas posições será bem definido e nunca atribuirá o mesmo nome a dois caracteres. Em moon e deed, as posições 0 e 3 contêm letras diferentes em s, mas duas vezes d em t, então a resposta é false.
Há cerca de n²/2 pares: 2 × 10^8 comparações quando n = 2 × 10^4, caso nenhum par interrompa o loop antes do fim.
Algoritmo
- Percorra todos os índices
ie, para cada um, todos os índices posterioresj. - Verifique se
s[i]é igual as[j]e set[i]é igual at[j]. - Se as duas respostas forem diferentes, retorne
false. - Após o último par, retorne
true.
def isIsomorphic(s, t):
n = len(s)
for i in range(n):
for j in range(i + 1, n):
# Two positions must hold equal characters in s exactly when they do in t
if (s[i] == s[j]) != (t[i] == t[j]):
return False
return TrueDuas maps, uma para cada direção
Intuição
Construa o mapeamento de renomeação enquanto lê. sToT registra em que cada caractere de s se tornou, e tToS registra de onde veio cada caractere de t. Na posição i, com a = s[i] e b = t[i], ou ambos são novos e você os associa, ou eles já devem apontar um para o outro.
Um mapa não basta. Usando apenas sToT, moon e deed são aceitos, porque nenhuma letra de s é renomeada de duas maneiras. O problema é que m e n se tornam d, e, na posição 3, tToS já indica que d veio de m.
Cada posição custa algumas operações de mapa, então o tempo é O(n). O código de cada caractere é menor que 128, então cada mapa pode ser um array com 128 entradas: espaço O(1). As versões em R e Swift primeiro copiam as strings para arrays de códigos, o que leva O(n).
Algoritmo
- Crie dois mapas vazios,
sToTetToS. - Para cada índice
i, definaa = s[i]eb = t[i]. - Se
anão estiver emsToTebnão estiver emtToS, mapeieaparabebparaa. - Caso contrário, se
sToT[a]não forboutToS[b]não fora, retornefalse. - Após o último índice, retorne
true.
def isIsomorphic(s, t):
s_to_t = {} # the character of t that each character of s became
t_to_s = {} # the character of s that each character of t came from
for a, b in zip(s, t):
if a not in s_to_t and b not in t_to_s:
s_to_t[a] = b
t_to_s[b] = a
elif s_to_t.get(a) != b or t_to_s.get(b) != a:
return False # a already has another target, or b another source
return True
Armadilhas e casos extremos
- Mantém um único mapeamento de
sparat. Ele aceitamoonedeed, eabeaa. - Compara quantas vezes cada caractere aparece.
abcabcexyzxzyusam ambos três caracteres duas vezes cada, mas o segundobfica sobrez, enquanto o primeiro ficou sobrey. - Trata maiúsculas e minúsculas como iguais.
aAcontém dois caracteres diferentes, então não pode se tornarbb. - Dimensiona os mapas do array apenas para
aaz. Letras maiúsculas e dígitos ficam fora desse intervalo; use o código completo do caractere como índice, com 128 posições.
Perguntas frequentes4
Qual é a complexidade de tempo de Strings Isomórficas?
A solução com dois mapas lê cada posição uma vez, então é executada em O(n) de tempo. Os mapas contêm no máximo uma entrada por caractere possível: 62 letras e dígitos; portanto, o espaço extra é O(1). Comparar cada par de posições leva O(n²).
Por que você precisa de dois mapas hash para strings isomórficas?
Um mapeamento de s para t detecta um caractere de s renomeado de duas maneiras. Ele não consegue detectar dois caracteres de s renomeados para o mesmo caractere, como em ab e aa. O mapeamento de t de volta para s detecta esse caso.
O isomorfismo é simétrico?
Sim. Uma renomeação um a um pode ser desfeita, então, se s for renomeado para t, inverter cada nome transforma t de volta em s. isIsomorphic(s, t) e isIsomorphic(t, s) sempre concordam.
De que outra forma você pode verificar se duas strings são isomórficas?
Substitua cada caractere pela posição em que ele aparece pela primeira vez e compare os resultados. noon e deed se tornam 0 1 1 0, enquanto moon se torna 0 1 1 3. Isso também leva O(n) de tempo, com O(n) de memória para os dois padrões.
Problemas parecidos
Problemas que usam as mesmas ideias. Resolver dois ou três é o que fixa um padrão.
def isIsomorphic(s, t):
# Escreva o código aquiTeste #1
Teste #2
Teste #3
Argumentos
s = "noon" t = "deed"
Saída Esperada
true