Contagem de Subarrays com Soma K

Este problema pede para contar quantos subarrays conttínuos em um array dado somam um valor específico k.

Abordagem 1: Soma de Prefixos Bruta

Uma maneira direta é calcular a soma de prefixos de todo o aray. A soma de um subarray de índice i a j (inclusive) pode ser encontrada subtraindo a soma de prefixo até i-1 da soma de prefixo até j. Iteramos por todas as combinações possíveis de i e j e verificamos se a diferença das somas de prefixo é igual a k.


class SolucaoSomaPrefixoBruta {
public:
    int contarSubarraySoma(vector<int>& nums, int k) {
        int tamanho = nums.size();
        vector<int> prefixoSomas(tamanho + 1, 0);
        int contador = 0;

        // Calcula as somas de prefixo
        for (int i = 0; i < tamanho; ++i) {
            prefixoSomas[i + 1] = prefixoSomas[i] + nums[i];
        }

        // Verifica todos os subarrays possíveis
        for (int i = 0; i <= tamanho; ++i) {
            for (int j = i + 1; j <= tamanho; ++j) {
                if (prefixoSomas[j] - prefixoSomas[i] == k) {
                    contador++;
                }
            }
        }
        return contador;
    }
};

Abordagem 2: Soma de Prefixos com Mapa de Hash

A abordagem anterior tem uma complexidade de tempo de O(n²), o que pode ser muito lento para entradas grandes. Podemos otimizar isso usando um mapa de hash para armazenar as frequências das somas de prefixo encontradas até agora. A ideia é que, se a soma de prefixo até o índice atual for somaAtual, e quisermos que a soma de um subarray terminando neste índice seja k, então a soma de prefixo anterior necessária seria somaAtual - k. Podemos verificar no mapa de hash quantas vezes essa soma de prefixo anterior ocorreu. Se ocorreeu x vezes, isso significa que existem x subarrays terminando no índice atual que somam k.

Inicializamos o mapa de hash com {0, 1} para lidar com casos em que um subarray começando do início do array soma k.


#include <vector>
#include <unordered_map>

class SolucaoHashSomaPrefixo {
public:
    int contarSubarraySoma(vector<int>& nums, int k) {
        int tamanho = nums.size();
        unordered_map<int, int> frequenciaSomaPrefixo;
        int somaAtual = 0;
        int contador = 0;

        frequenciaSomaPrefixo[0] = 1; // Para casos onde o subarray começa do índice 0

        for (int i = 0; i < tamanho; ++i) {
            somaAtual += nums[i];
            int diferenca = somaAtual - k;

            // Se encontramos uma soma de prefixo anterior que, quando subtraída
            // da soma atual, resulta em k, adicionamos a contagem dessa soma
            // de prefixo anterior ao nosso resultado.
            if (frequenciaSomaPrefixo.count(diferenca)) {
                contador += frequenciaSomaPrefixo[diferenca];
            }

            // Incrementa a frequência da soma de prefixo atual no mapa
            frequenciaSomaPrefixo[somaAtual]++;
        }

        return contador;
    }
};

Considerações sobre Implementação

Na implementação original, a diferença entre as variáveis membro (res em uma versão) e as variáveis locais pode afetar o desempenho devido a otimizações do compilador. Variáveis locais geralmente podem ser alocadas em registradores, tornando o acesso mais rápido do que acessar variáveis membro que podem exigir desreferenciamento de ponteiro this. Para problemas com grandes restrições de tempo, a abordagem de mapa de hash O(n) é altamente recomendada em vez das abordagens O(n²) que podem levar a tempos limite (Time Limit Exceeded - TLE).

Tags: Algoritmos Estruturas de Dados Mapa de Hash Soma de Prefixos Array

Publicado em 7-3 22:28