terça-feira, 2 de janeiro de 2018

Problema dos anagramas através de uma função recursiva


Compartilhe!

O objetivo deste programa em C abaixo é gerar todos os anagramas de uma palavra.

Um anagrama de uma palavra é uma permutação das suas letras a fim de formar uma outra palavra. Por exemplo, ator é um anagrama da palavra rota.

A saída deve conter todos os anagramas possíveis, sem repetições e em ordem alfabética, para uma palavra através de uma função recursiva que deve ser implementada em um arquivo separado da main.

Exemplo de entrada:
ave

Exemplo de saída:
aev
ave
eav
eva
vae
vea

Arquivo com a função recursiva geradora dos anagramas:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*Função generate:
 * Entrada: 
 *          string_ordenada: a string original, que devemos processar para encontrarmos todos os anagramas
 *          letra_usada: um vetor de booleanos (implementado da forma de um vetor de inteiros) para marcarmos quais posições da string original já foram usadas
 *          word: o anagrama que estamos formando
 *          n: o numero de letras do anagrama
 *          k: a posição onde incluiremos a proxima letra em word
 * 
 * A ideia para gerar sem repeticoes eh que setada a letra da posicao k,
 * e retornado da chamada recursiva, temos que colocar uma letra diferente na posição k, 
 * pois senão geraríamos as repetições. Quando n==k incluimos a letra faltante e imprimimos o
 * anagrama.
 */

void generate(char *string_ordenada, int *letra_usada, char *word, int n, int k){

 if(k<n){//Se há mais letras a ser adicionada (quando k é menor que n):

   for(int i=0; i<n;i++)//Percorre o vetor booleano de quais letras foram usadas e quais não
    if(letra_usada[i]==0){//Se a letra na posição i não foi usada:

     letra_usada[i] = 1;//Marque-a como usada,
     word[k] = string_ordenada[i];//Armazene em word na posição k a letra correspondente,
     generate(string_ordenada, letra_usada, word, n, k+1);//E chame a própria função.

     letra_usada[i]=0;//Torne a letra atual como não usada,
     for(;string_ordenada[i] == string_ordenada[i+1];i++);//E verifique qual é a próxima letra que seja diferente da atual, avançando a posição i.

    }

 }else{//Caso já todas as letras tenham sido usadas:
 
   word[n]='\0';//Adicione o '\0', indicador de fim da string,
   printf("%s\n",word);//E imprima a string/palavra/anagrama formado.
   
 }

}

No código abaixo, que, novamente, seria um arquivo separado, temos nossa função main (de autoria dos PEDs do IC-Unicamp):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
A ideia para gerar sem repeticoes e que setado a letra da posicao k,
geramos todas as possibilidades e quando voltar do backtracking temos
que colocar uma letra diferente na posição k, pois senão geraríamos
as repetições. Por isso depois de setado uma posição a próxima letra
da posição é dado por proxima_letra_diferente que discarta letras iguais a já setada.
 */
void generate(char *string_ordenada, int *letra_usada, char *word, int n, int k);

char * sort(char string[]);

int main(){

  char *string_inicial;
  string_inicial = (char*) malloc(sizeof(char));
  int string_inicial_tam = 0;
  char c;
  while(scanf("%c", &c) && c != '\n') {
    string_inicial_tam++;
    string_inicial = (char*) realloc(string_inicial, string_inicial_tam+1 * sizeof(char));
    string_inicial[string_inicial_tam-1] = c;
    string_inicial[string_inicial_tam] = '\0';
  }
  char *string_ordenada =  sort(string_inicial);
  int n  = strlen(string_ordenada);  
  int *letra_usada = malloc(n*sizeof(int));
  char *anagrama = malloc((n+1)*sizeof(char));

  int i=0;
  for(i=0; i<n; i++)
    letra_usada[i]=0;
  generate(string_ordenada, letra_usada, anagrama, n, 0);

  free(anagrama);
  free(string_ordenada);
  free(letra_usada);
}

char * sort(char string[]){
  int count[256], i;
  char *sr;

  for(i=0; i<256; i++)
    count[i] = 0;
  
  i=0;
  while(string[i] != '\0'){
    count[(int)string[i]]++;
    i++;
  }
  
  sr = malloc((i+1)*sizeof(char));
  
  int j=0, k=0;
  for(i=0; i<256; i++){
    if(count[i]!=0){
      for(k=0; k<count[i]; k++){
 sr[j] = i;
 j++;
      }
    }
  }
  sr[j] = '\0';
  return sr;
}