2 votes

Testeur d'anagrammes en C

J'essaie d'implémenter un testeur d'anagrammes en C. Lors de l'appel du programme, l'utilisateur entre deux mots entre guillemets, comme "listen" et "silent". J'ai presque réussi à le faire fonctionner, mais j'ai quelques problèmes avec une fonction d'aide que j'ai écrite pour éliminer les espaces dans les deux mots entrés. Voici le code de cette fonction :

void noSpaces(char word[100]) {
    /*
    This is a function to get rid of spaces in a word
    It does this by scanning for a space and shifting the
    array elements at indices > where the space is
    down by 1 as long as there is still a space
    there. 
    */
    for (int i = 0; i < 100; i++) {
        while (word[i] == ' ') {
            for (int j = i; j < 100; j++) {
                word[j] = word[j+1];
            }
        }
    }
}

Maintenant, cela fonctionne bien lorsque je passe le mot d'entrée de la fonction main à cette aide. Le problème se situe au niveau du deuxième appel à cette fonction. Lorsque j'appelle cette fonction sur la deuxième entrée, si k est le nombre d'espaces dans la première entrée, alors la fonction efface la première entrée. k lettres de la deuxième entrée. Par exemple, en tapant ./anagram " banana" "banana" me donnera un faux négatif, et si j'ajoute une instruction print pour voir ce qui se passe avec les entrées après noSpaces est appelé sur eux, j'obtiens ce qui suit :

banana
anana

Voici le code pour le programme complet :

#include <stdio.h>

int main(int argc, char *argv[]) {
    //this if statement checks for empty entry
    if (isEmpty(argv[1]) == 0 || isEmpty(argv[2]) == 0) {
        //puts("one of these strings is empty");
        return 1;
    }
    //call to noSpaces to eliminate spaces in each word
    noSpaces(argv[1]);
    noSpaces(argv[2]);
    //call to sortWords
    sortWords(argv[1]);
    sortWords(argv[2]);
    int result = compare(argv[1], argv[2]);
    /*
    if (result == 1) {
        puts("Not anagrams");
    } else {
        puts("Anagrams");
    }
    */
    return result;
}

int compare(char word1[100], char word2[100]) {
    /*
    This is a function that accepts two sorted 
    char arrays (see 'sortWords' below) and
    returns 1 if it finds a different character
    at entry i in either array, or 0 if at no 
    index the arrays have a different character.
    */
    int counter = 0;
    while (word1[counter] != '\0' && word2[counter] != '\0') {
        if (word1[counter] != word2[counter]) {
            //printf("not anagrams\n");
            return 1;
        }
        counter++;
    }
    // printf("anagrams\n");
    return 0;
}

void sortWords(char word[100]) {
    /*
    This is a function to sort the input char arrays
    it's a simple bubble sort on the array elements.
    'sortWords' function accepts a char array and returns void,
    sorting the entries in alphabetical order
    being careful about ignoring the 'special character'
    '\0'.
    */
    for (int j = 0; j < 100; j++) {
        int i = 0;
        while (word[i + 1] != '\0') {
            if (word[i] > word[i + 1]) {
                char dummy = word[i + 1];
                word[i + 1] = word[i];
                word[i] = dummy;
            }
            i++;
        }
    }
}

void noSpaces(char word[100]) {
    /*
    This is a function to get rid of spaces in a word
    It does this by scanning for a space and shifting the
    array elements at indices > where the space is
    down by 1 as long as there is still a space there. 
    */
    for (int i = 0; i < 100; i++) {
        while (word[i] == ' ') {
            for (int j = i; j < 100; j++) {
                word[j] = word[j + 1];
            }
        }
    }
}

int isEmpty(char word[100]) {
    // if a word consists of the empty character, it's empty
    //otherwise, it isn't
    if (word[0] == '\0') {
        return 0;
    }
    return 1;
}

Je sais qu'il existe une bibliothèque qui peut traiter les chaînes de caractères, mais j'ai vraiment mais j'aimerais vraiment éviter d'avoir à l'utiliser. J'ai déjà parcouru tout ce chemin sans en avoir besoin, et je pense que le problème est en grande partie résolu, à l'exception d'une petite chose que je ne vois pas.

Je viens d'un milieu Java, et je suis nouveau en C, si cela explique l'erreur que j'ai commise.

1voto

okovko Points 327

Vous faites une erreur de logique dans votre fonction d'aide. Vous commencez la copie à partir de word[j] plutôt qu'au début du deuxième mot, ce qui signifie que vous allez supprimer autant de caractères de tête que d'espaces de tête, comme vous le voyez dans le résultat.

Notez que j=i y i compte le nombre d'espaces avant de la boucle externe.

Au fait, vous ne devriez avoir que deux boucles. Mettez le while à l'intérieur de la première for une boucle comme celle-ci : for (int i = 0; i<100 && word[i]==' '; i++) .

Pour corriger votre erreur de logique, vous devez utiliser un autre itérateur k initialisée à zéro dans la boucle la plus interne, et utiliser la fonction word[k] = word[j+1] . Je pense que ça va marcher.

1voto

Loc Tran Points 1190

Vous avez un problème de dépassement de tampon sur argv[1] et argv[2] dans le cas où la longueur du tampon de argv[1] est inférieure à 100. Je pense donc que vous devriez utiliser la boucle for avec strlen(word) qui est suffisant. Si vous utilisez une longueur statique de 100 dans la boucle for, il peut arriver que le mot obtienne les données d'un autre emplacement mémoire et que votre programme ait un comportement indéfini. D'autres fonctions ont également le même problème. Je veux dire la fonction SortWords y comparer fonctions.

Voici ma modification dans votre fonction noSpaces, elle devrait fonctionner.

void noSpaces(char word [100]){
    /*
    This is a function to get rid of spaces in a word
    It does this by scanning for a space and shifting the
    array elements at indices > where the space is
    down by 1 as long as there is still a space
    there.
    */
    for(int i =0; i<strlen(word)-1; i++){
        while(word[i]==' '){
            for(int j = i ; j<strlen(word); j++){
                word[j] = word [j+1];
            }
        }
    }
}

1voto

chqrlie Points 17105

En C, les chaînes de caractères sont des tableaux de char avec un terminateur nul, c'est-à-dire un octet avec la valeur 0 généralement représenté par '\0' . Vous ne devez pas supposer une longueur particulière comme 100 . En effet, la taille du tableau dans les arguments du prototype de la fonction est ignorée par le compilateur. Vous pouvez déterminer la longueur en recherchant le terminateur nul, ce qui est le cas de strlen() ou vous pouvez écrire le code de manière à éviter les balayages multiples, en vous arrêtant au terminateur nul. Vous devez vous assurer que vos fonctions fonctionnent pour la chaîne vide, qui est un tableau avec un seul octet nul. Voici les problèmes de votre code :

En fonction noSpaces vous itérez au-delà de la fin de la chaîne, en modifiant la mémoire appartenant potentiellement à la chaîne suivante. Le programme a un comportement indéfini.

Vous devez vous arrêter à la fin de la chaîne. Utilisez également 2 variables d'index pour effectuer en temps linéaire :

void noSpaces(char word[]) {
    /*
    This is a function to get rid of spaces in a word
    It does this by scanning for a space and shifting the
    array elements at indices > where the space is
    down by 1 as long as there is still a space
    there. 
    */
    int i, j;
    for (i = j = 0; word[i] != '\0'; i++) {
        if (word[i] != ' ') {
            word[j++] = word[i];
        }
    }
    word[j] = '\0';
}

Vous pouvez simplifier compare d'utiliser un tiers des tests en moyenne :

int compare(const char word1[], const char word2[]) {
    /*
    This is a function that accepts two sorted 
    char arrays (see 'sortWords' below) and
    returns 1 if it finds a different character
    at entry i in either array, or 0 if at no 
    index the arrays have a different character.
    */
    for (int i = 0; word1[i] == word2[i]; i++) {
        if (word1[i]) == '\0')
            //printf("anagrams\n");
            return 0;
        }
    }
    // printf("not anagrams\n");
    return 1;
}

sortWords a un comportement indéfini pour la chaîne vide parce que vous lisez le fichier char à l'indice 1 au-delà de l'extrémité du tableau. Voici une version corrigée :

void sortWords(char word[]) {
    /*
    This is a function to sort the input char arrays
    it's a simple bubble sort on the array elements.
    'sortWords' function accepts a char array and returns void,
    sorting the entries in alphabetical order
    being careful about ignoring the 'special character'
    '\0'.
    */
    for (int j = 0; word[j] != '\0'; j++) {
        for (int i = 1; i < j; i++) {
            if (word[i - 1] > word[i]) {
                char dummy = word[i - 1];
                word[i - 1] = word[i];
                word[i] = dummy;
            }
        }
    }
}

Vous devez déclarer les fonctions avant de les utiliser, ou alternativement les définir avant de les utiliser. Votre code se compile parce que le compilateur accepte l'ancien style de C où le prototype de fonctions encore inconnues était déduit des arguments passés au premier site d'appel. Cette pratique est source d'erreurs et obsolète.

Votre fonction de tri a une complexité temporelle quadratique, ce qui peut être très lent pour les chaînes très longues, mais les mots ne devraient pas être trop grands, donc ce n'est pas un problème.

Il serait préférable de ne pas modifier les chaînes d'arguments. Vous pouvez effectuer le test avec une copie de l'une des chaînes de caractères avec la même complexité temporelle.

Voici une approche directe :

#include <stdio.h>

int check_anagrams(const char word1[], const char word2[]) {
    /*
       This function accepts two strings and returns 1 if they
       are anagrams of one another, ignoring spaces.
       The strings are not modified.
     */
    int i, j, len1, letters1, letters2;

    /* compute the length and number of letters of word1 */
    for (len1 = letters1 = 0; word1[len1] != '\0'; len1++) {
        if (word1[len1] != ' ')
            letters1++;
    }

    /* create a copy of word1 in automatic storage */
    char copy[len1];    /* this is an array, not a string */
    for (i = 0; i < len1; i++)
        copy[i] = word1[i];

    for (j = letters2 = 0; word2[j] != '\0'; j++) {
        char temp = word2[j];
        if (temp != ' ') {
            letters2++;
            for (i = 0; i < len1; i++) {
                if (copy[i] == temp) {
                    copy[i] = '\0';
                    break;
                }
            }
            if (i == len1) {
                /* letter was not found */
                return 0;
            }
        }
    }
    if (letters1 != letters2)
        return 0;
    return 1;
}

int main(int argc, char *argv[]) {
    const char *s1 = " listen";
    const char *s2 = "silent   ";
    if (argc >= 3) {
        s1 = argv[1];
        s2 = argv[2];
    }
    int result = check_anagrams(s1, s2);
    if (result == 0) {
        printf("\"%s\" and \"%s\" are not anagrams\n", s1, s2);
    } else {
        printf("\"%s\" and \"%s\" are anagrams\n", s1, s2);
    }
    return result;
}

0voto

selbie Points 20267

Plutôt que d'essayer de supprimer les espaces et de trier, ce qui représente un temps d'exécution O(N lg N). Vous pouvez faire une opération O(N) en construisant simplement un tableau qui représente le nombre de chaque lettre dans un mot. Et il suffit d'ignorer les espaces pendant cette opération.

// Iterate over each character in the string
// For each char in string, increment the count of that character
// in the lettercount array.
// Return the number of unfiltered letters that were counted
int fillLetterCountTable(const char* string, int* lettercount)
{
    int len = strlen(string);
    int valid = 0;

    for (int i = 0; i < len; i++)
    {
       unsigned char index = (unsigned char)(string1[i]);
       if (index ==  ' ')  // ignore spaces
       {
           continue;
       }
       counts[index] += 1;
       valid++;
    }

    return valid;
}

// compare if two strings are anagrams of each other
// return true if string1 and string2 are anagrams, false otherwise
bool compare(const char* string1, const char* string2)
{
    int lettercount1[256] = {0};
    int lettercount2[256] = {0};

    int valid1 = fillLetterCountTable(string1, lettercount1);
    int valid2 = fillLetterCountTable(string2, lettercount2);

    if (valid1 != valid2)
        return false;

    // memcmp(lettercount1, lettercount2, sizeof(lettercount1));
    for (int i = 0; i < 256; i++)
    {
        if (counts1[i] != counts2[i])
            return false;
    }
    return true;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X