Escrever um programa em C que calcule a frequência de uso de cada palavra dentro de uma cadeia de caracteres que possui diversas palavras. Considerar o tamanho máximo da cadeia como sendo de 80 colunas.

Passo 1

O enunciado pede: escrever um programa em C que calcule a frequência de uso de cada palavra dentro de uma cadeia de caracteres que possui diversas palavras. O tamanho máximo da cadeia é de 80 colunas. \n
Informações/dados relevantes (explicitando suposições que vou usar para resolver o problema, já que o enunciado não detalha alguns pontos):\n
  • Vou assumir que a entrada é uma única linha de texto com no máximo 80 caracteres (mais o caractere nulo).\n
  • Defino “palavra” como uma sequência de caracteres alfanuméricos (letras e dígitos). Ou seja, qualquer caractere que não seja letra nem dígito funciona como separador (espaço, pontuação, etc.).\n
  • O reconhecimento de palavras será feito de forma case-insensitive (não diferencio maiúsculas/minúsculas). Assim, “Teste”, “teste” e “TESTE” contam como a mesma palavra.\n
  • Vou armazenar as palavras distintas usando um vetor de estruturas com espaço suficiente para, no pior caso, uma palavra por caractere (ou seja, até 80 palavras), cada palavra com tamanho máximo 80.\n
  • O programa vai imprimir cada palavra seguida da sua contagem, na ordem em que as palavras distintas aparecem pela primeira vez na linha.\n

Passo 2

Estratégia/algoritmo (visão geral passo a passo):\n
  1. Ler a linha de entrada usando uma função segura como \verb|fgets| para garantir que não ultrapassamos o limite de 80 caracteres.\n
  2. Percorrer a linha caractere a caractere e construir tokens (palavras) quando encontrarmos caracteres alfanuméricos; ao encontrar um separador, fechar o token atual (se existir) e processá-lo.\n
  3. Para cada token obtido (já normalizado para minúsculas), procurar no vetor de palavras já armazenadas: se encontrado, incrementar o contador; se não encontrado, adicionar uma nova entrada com contador 1.\n
  4. Ao fim do processamento, imprimir as palavras e suas frequências.\n

Passo 3

Estruturas de dados e funções C que vamos usar (explicando didaticamente):\n
  • Arrays de char (strings em C): uma string é um array de caracteres terminado por '\0'. Para armazenar até 80 caracteres utilizamos um array com tamanho 81 para deixar espaço ao terminador nulo.\n
  • struct para palavra e contagem: uma estrutura com um campo char word[81] e int count permite guardar a palavra e quantas vezes ela apareceu.\n
  • Função \verb|fgets(buffer, size, stdin)|: lê no máximo size-1 caracteres e coloca um '\0' ao final; também capta o '\n' se houver.\n- Funções de ctype.h: \verb|isalnum(c)| diz se o caractere é letra ou dígito; \verb|tolower(c)| converte caractere para minúsculo. Importantíssimo: passar unsigned char para essas funções para evitar UB com chars negativos.\n- Funções de string.h: \verb|strcmp(a,b)| compara strings; \verb|strcpy(dest, src)| copia strings; \verb|strlen(s)| dá o comprimento (não usado obrigatoriamente aqui).\n\nExplicação prática curta: ao montar um token, vamos usar um pequeno buffer token[]. Quando um separador aparece, terminamos token com '\0' e buscamos no vetor de structs usando strcmp. Se igual, incrementamos count, senão usamos strcpy para gravar a nova palavra no vetor.\n

Passo 4

Pseudocódigo detalhado (para ajudar a transformar em C sem surpresas):\n
  • Inicializar words_count = 0 (número de palavras distintas armazenadas).\n- Ler a linha com fgets(line, sizeof line, stdin).\n- Para cada caractere c da linha (incluindo o '\0' no final):\n - Se isalnum\text{(c)}: acrescentar tolower\text{(c)} ao token (se houver espaço no token).\n - Senão (é separador): se token não estiver vazio, então:\n - terminar token com '\0';\n - procurar token em words[0..words_count-1] com strcmp;\n - se achar incrementa count; se não achar, copiar token para words[words_count].word, setar count = 1 e words_count++;\n - esvaziar token (resetar comprimento para 0).\n- Depois do loop, pode haver token pendente se a linha terminou com caractere alfanumérico: tratar do mesmo modo.\n- Imprimir todas as entradas de words com printf("%s: %d\n", word, count).\n

Passo 5

Decisões e alternativas (por que escolhi este método):\n
  • Poderíamos usar tabelas de hash para acelerar buscas (bom para grandes textos), mas o enunciado limita a linha a 80 colunas e é questão de graduação básica — então um vetor com busca linear é simples, fácil de implementar na prova e suficiente em desempenho para esse tamanho.\n- Optei por normalizar para minúsculas para tratar casos de capitalização. Poderíamos tratar case-sensitive, mas isso tornaria a resposta menos útil na maioria dos exercícios práticos.\n

Passo 6

Boas práticas e cuidados (o que observar na hora da implementação na prova):\n
  • Sempre proteger leituras com limites (usar fgets com o tamanho do buffer).\n- Ao usar funções de ctype.h converta o char para unsigned char antes de passar para isalnum/tolower, por segurança.\n- Garantir que os buffers de palavras têm espaço para o terminador nulo '\0'.\n- Evitar overflow ao acrescentar caracteres ao token: verificar o comprimento máximo.\n- Lembre-se de tratar o token que pode ficar no buffer ao atingir o fim da string (quando o último caractere é alfanumérico).\n

Passo 7

Programa em C com comentários explicativos (este é o código que você escreveria na prova). Ele lê uma linha (até 80 caracteres), conta a frequência das palavras e imprime o resultado na ordem de primeira aparição:
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define MAX_LINE 80
#define MAX_WORDS 80
#define MAX_WORD_LEN 80

typedef struct {
    char word[MAX_WORD_LEN + 1];
    int count;
} Word;

int main(void) {
    char line[MAX_LINE + 2]; /* +2 para '\n' possível e '\0' */
    Word words[MAX_WORDS];
    int words_count = 0;

    /* Inicializa contadores */
    for (int i = 0; i < MAX_WORDS; i++) {
        words[i].word[0] = '\0';
        words[i].count = 0;
    }

    /* Lê a linha de entrada */
    if (fgets(line, sizeof(line), stdin) == NULL) {
        return 0; /* sem entrada */
    }

    /* Processa caractere a caractere */
    char token[MAX_WORD_LEN + 1];
    int tlen = 0;

    for (int i = 0; ; i++) {
        unsigned char c = (unsigned char) line[i];

        /* termina quando alcança o caractere nulo */
        if (c == '\0') {
            if (tlen > 0) {
                token[tlen] = '\0';
                /* processa token final */
                int found = 0;
                for (int j = 0; j < words_count; j++) {
                    if (strcmp(words[j].word, token) == 0) {
                        words[j].count++;
                        found = 1;
                        break;
                    }
                }
                if (!found && words_count < MAX_WORDS) {
                    strcpy(words[words_count].word, token);
                    words[words_count].count = 1;
                    words_count++;
                }
                tlen = 0;
            }
            break;
        }

        if (isalnum\text{(c)}) {
            /* adicionar caractere em minúscula ao token, se houver espaço */
            if (tlen < MAX_WORD_LEN) {
                token[tlen++] = (char) tolower\text{(c)};
            }
            /* se token atingir MAX_WORD_LEN, ignoramos excesso para evitar overflow */
        } else {
            /* separador encontrado: se havia token, processá-lo */
            if (tlen > 0) {
                token[tlen] = '\0';
                int found = 0;
                for (int j = 0; j < words_count; j++) {
                    if (strcmp(words[j].word, token) == 0) {
                        words[j].count++;
                        found = 1;
                        break;
                    }
                }
                if (!found && words_count < MAX_WORDS) {
                    strcpy(words[words_count].word, token);
                    words[words_count].count = 1;
                    words_count++;
                }
                tlen = 0;
            }
            /* caso contrário, apenas ignoramos sequências de separadores */
        }
    }

    /* Imprime resultado: palavra: frequência */
    for (int i = 0; i < words_count; i++) {
        printf("%s: %d\n", words[i].word, words[i].count);
    }

    return 0;
}

Passo 8

Explicação do código e exemplo rápido:\n
  • O loop principal percorre a string line até o '\0'.\n- isalnum detecta letras e dígitos; quando isso acontece adicionamos o caractere convertido por tolower ao token (garantindo não ultrapassar MAX_WORD_LEN).\n- Ao encontrar um separador (ou o fim da linha), se o token tiver conteúdo, comparamos linearmente com as words já registradas; se achar, incrementamos count, senão criamos uma nova entrada.\n- Impressão final mostra cada palavra com seu número de ocorrências na ordem de primeira aparição.\n\nExemplo de uso (entrada -> saída):\nEntrada:\nThis is a test. This test is simple!\nSaída esperada (cada linha palavra: frequência):\nthis: 2\nis: 2\na: 1\ntest: 2\nsimple: 1\n\nObservação: os resultados aparecem em minúsculas porque normalizamos tudo para minúsculas.\n

Resposta

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX_LINE 80
#define MAX_WORDS 80
#define MAX_WORD_LEN 80
typedef struct {
char word[MAX_WORD_LEN + 1];
int count;
} Word;
int main(void) {
char line[MAX_LINE + 2]; /* +2 para ‘\n’ possível e ‘\0’ */
Word words[MAX_WORDS];
int words_count = 0;
/* Inicializa contadores */
for (int i = 0; i < MAX_WORDS; i++) {
    words[i].word[0] = '\0';
    words[i].count = 0;
}

/* Lê a linha de entrada */
if (fgets(line, sizeof(line), stdin) == NULL) {
    return 0; /* sem entrada */
}

/* Processa caractere a caractere */
char token[MAX_WORD_LEN + 1];
int tlen = 0;

for (int i = 0; ; i++) {
    unsigned char c = (unsigned char) line[i];

    /* termina quando alcança o caractere nulo */
    if (c == '\0') {
        if (tlen > 0) {
            token[tlen] = '\0';
            /* processa token final */
            int found = 0;
            for (int j = 0; j < words_count; j++) {
                if (strcmp(words[j].word, token) == 0) {
                    words[j].count++;
                    found = 1;
                    break;
                }
            }
            if (!found && words_count < MAX_WORDS) {
                strcpy(words[words_count].word, token);
                words[words_count].count = 1;
                words_count++;
            }
            tlen = 0;
        }
        break;
    }

    if (isalnum\text{(c)}) {
        /* adicionar caractere em minúscula ao token, se houver espaço */
        if (tlen < MAX_WORD_LEN) {
            token[tlen++] = (char) tolower\text{(c)};
        }
        /* se token atingir MAX_WORD_LEN, ignoramos excesso para evitar overflow */
    } else {
        /* separador encontrado: se havia token, processá-lo */
        if (tlen > 0) {
            token[tlen] = '\0';
            int found = 0;
            for (int j = 0; j < words_count; j++) {
                if (strcmp(words[j].word, token) == 0) {
                    words[j].count++;
                    found = 1;
                    break;
                }
            }
            if (!found && words_count < MAX_WORDS) {
                strcpy(words[words_count].word, token);
                words[words_count].count = 1;
                words_count++;
            }
            tlen = 0;
        }
        /* caso contrário, apenas ignoramos sequências de separadores */
    }
}

/* Imprime resultado: palavra: frequência */
for (int i = 0; i < words_count; i++) {
    printf("%s: %d\n", words[i].word, words[i].count);
}

return 0;
}

Exercícios relacionados