Pular para o conteudo principal

Maior substring sem caracteres repetidos

Como perceber que o problema pede uma janela que expande e contrai, não um recomeco completo a cada repetição.

Dada uma string s, retorne o comprimento da maior substring sem caracteres repetidos.

Exemplo:

Input:  s = "abcabcbb"
Output: 3

A maior substring sem repetição e "abc", com comprimento 3.

O que perceber antes de codar

Erros comuns

Passo 1 — Entender o que inválida a substring

Enquanto todos os caracteres forem unicos, a janela atual continua valida.

O problema começa quando aparece uma repetição.

A pergunta não e "qual substring testar agora?".

A pergunta e:

como manter uma janela valida enquanto ando pela string?

Passo 2 — Escrever a versão inicial correta

Uma versão inicial boa e fixar um início e estender o fim até repetir:

export function lengthOfLongestSubstring(s: string): number {
  let best = 0

  for (let start = 0; start < s.length; start += 1) {
    const seen = new Set<string>()

    for (let end = start; end < s.length; end += 1) {
      const char = s[end]

      if (seen.has(char)) {
        break
      }

      seen.add(char)
      best = Math.max(best, end - start + 1)
    }
  }

  return best
}

Funciona. Mas recomeça trabalho demais para cada início.

Passo 3 — Perguntar o que realmente precisa sair da janela

Se right encontrou um caractere repetido, precisa limpar tudo?

Não.

Só precisa remover da esquerda até aquele caractere deixar de estar duplicado.

Isso e o coracao de uma janela deslizante.

Passo 4 — Expandir e contrair sob regra

Use:

  • right para expandir
  • left para contrair quando a regra quebra
  • conjunto hash para representar os caracteres dentro da janela atual

A janela só anda para frente. Não precisa voltar.

O editor interativo precisa de JavaScript. Voce ainda pode ler o desafio e copiar o codigo inicial abaixo.

Codigo inicial

export function lengthOfLongestSubstring(s: string): number {
  // sua solução aqui
  return 0
}
solution.ts
Travado? Dicas disponiveis.

Ainda não resolveu?

Ver a solução agora pode reduzir o aprendizado. Vale a pena tentar mais um pouco.

Abrir a solucao de referencia

Sem JavaScript, a solucao de referencia aparece inline em vez de um dialogo.

Solução

Complexidade final

Tempo: O(n)

Espaço: O(min(n, charset))

Solução 1 — Testar cada início

Boa versão inicial para enxergar a regra.

export function lengthOfLongestSubstring(s: string): number {
  let best = 0

  for (let start = 0; start < s.length; start += 1) {
    const seen = new Set<string>()

    for (let end = start; end < s.length; end += 1) {
      const char = s[end]

      if (seen.has(char)) {
        break
      }

      seen.add(char)
      best = Math.max(best, end - start + 1)
    }
  }

  return best
}

Solução 2 — Janela deslizante com conjunto hash em O(n)

Agora a janela se ajusta sem recomecar tudo.

export function lengthOfLongestSubstring(s: string): number {
  const window = new Set<string>()
  let left = 0
  let best = 0

  for (let right = 0; right < s.length; right += 1) {
    const char = s[right]

    while (window.has(char)) {
      window.delete(s[left])
      left += 1
    }

    window.add(char)
    best = Math.max(best, right - left + 1)
  }

  return best
}

Cada caractere entra e sai da janela no máximo uma vez.

O que dizer na entrevista

Uma explicação curta e boa seria:

A versão inicial testa cada início e custa O(n²). A versão otimizada mantem uma janela sem repetição. Eu expando com right e, quando aparece duplicata, contraio com left só o necessário até a janela voltar a ficar valida. Isso reduz para O(n).

Isso mostra que você:

  • entendeu a regra que inválida a janela
  • explicou a janela deslizante como movimento controlado
  • evitou tratar a otimização como template cego

Quando esse padrão aparece de novo

Esse padrão reaparece quando você precisa:

  • manter uma janela valida
  • expandir enquanto a regra vale
  • contrair até corrigir violacao
  • contar ou maximizar algo dentro de um intervalo movel

O ponto não e decorar que janela deslizante e igual a dois ponteiros.

O ponto e perceber quando o problema fala de uma faixa contigua com regra local.

Proximo desafio Três somas Desafio anterior Subindo escadas

Desafios relacionados

Artigos relacionados