Cálculo del carácter más frecuente en una cadena utilizando arreglos como tablas hash en C++

Planteamiento del Problema

Se proporciona una cadena compuesta exclusivamente por letras minúsculas del alfabeto inglés. El objetivo es calcular la frecuencia de aparición de cada letra y determinar cuál es la más frecuente. En caso de que existan múltiples letras con la misma frecuencia máxima, se debe devolver aquella que tenga precedencia lexicográfica (es decir, la que aparezca primero en el alfabeto).

Formato de entrada:

La entrada consta de múltiples casos de prueba. Cada caso comienza con un número entero que indica la cantidad de cadenas a procesar, seguido de las cadenas correspondientes, cada una en una línea separada.

Formato de salida:

Para cada cadena procesada, se debe imprimir en una línea independiente el carácter que cumple con las condiciones descritas.

Ejemplo de entrada:

2
abcdeef
aabbccddeeff

Ejemplo de salida:

e
a

Fundamentos de las Tablas Hash

Una tabla hash es una esturctura de datos que permite el acceso directo a los elementos mediante una clave (key). Su propósito principal es verificar rápidamente la existencia de un elemento dentro de un conjunto. Esto se logra estableciendo una relación determinista entre la clave y su posición de almacenamiento a través de una función matemática conocida como función hash o de dispersión.

Para implementar tablas hash, generalmente se recurre a tres estructuras de datos fundamentales:

  • Arreglos (Arrays)
  • Conjuntos (set)
  • Mapas o diccionarios (map)

Conceptualmente, una tabla hash funciona como un archivador con múltiples cajones numerados. La función hash transforma la clave en un índice numérico que apunta a un cajón específico donde se almacena el valor. Cuando surgen colisiones (diferentes claves que generan el mismo índice), se aplican estrategias de resolución para garantizar la integridad de los datos. La principal ventaja de esta estructura es su capacidad para realizar búsquedas, insercioens y eliminaciones en un tiempo promedio constante, O(1).

Uso de Arreglos como Tablas Hash

Dado que el problema restringe los caracteres a letras minúsculas del alfabeto inglés, el espacio de claves es finito y reducido (exactamente 26 caracteres). Esto convierte a un arreglo de tamaño fijo en la tabla hash más eficiente para este escenario.

Los caracteres en C++ están representados por sus valores ASCII, los cuales son consecutivos para las letras minúsculas ('a' hasta 'z'). Por lo tanto, podemos mapear cada letra a un índice del arreglo restando el valor ASCII de 'a' al valor ASCII del carácter actual. Así, 'a' se mapea al índice 0, 'b' al 1, y así sucesivamente hasta 'z' en el índice 25.

Al iterar sobre la cadena, simplemente incrementamos el contador en la posición correspondiente:

// Registro de frecuencias
for (char c : text) {
    frequencyMap[c - 'a']++;
}

Una vez poblado el arreglo, basta con recorrerlo de principio a fin (del índice 0 al 25). Al evaluar los valores con una condición de "estrictamente mayor" (>), nos aseguramos de que, en caso de empate, se conserve el carácter evaluado primero, garantizando así la precedencia lexicográfica requerida.

Implementación en C++

#include <iostream>
#include <string>
#include <vector>

int main() {
    // Optimización para acelerar las operaciones de entrada/salida estándar
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(NULL);

    int testCases;
    // Lectura continua de casos de prueba
    while (std::cin >> testCases) {
        while (testCases--) {
            std::string inputText;
            std::cin >> inputText;
            
            // Arreglo que actúa como tabla hash para las 26 letras minúsculas
            std::vector<int> frequencyMap(26, 0);
            
            // Cálculo de frecuencias usando un bucle basado en rangos
            for (char currentChar : inputText) {
                frequencyMap[currentChar - 'a']++;
            }
            
            int maxFrequency = -1;
            char mostFrequentChar = 'a';
            
            // Búsqueda de la frecuencia máxima
            // Al iterar desde 0 hasta 25 y usar '>', garantizamos el orden lexicográfico en empates
            for (int i = 0; i < 26; ++i) {
                if (frequencyMap[i] > maxFrequency) {
                    maxFrequency = frequencyMap[i];
                    mostFrequentChar = static_cast<char>(i + 'a');
                }
            }
            
            std::cout << mostFrequentChar << '\n';
        }
    }
    
    return 0;
}

Etiquetas: C++ hash-table array ASCII string-manipulation

Publicado el 7-19 10:22