Анаграмма тестер в Си - PullRequest
       18

Анаграмма тестер в Си

0 голосов
/ 16 февраля 2019

Я пытаюсь реализовать тестер анаграммы на C. При вызове программы пользователь вводит два слова в двойных кавычках, таких как «listen» и «silent».Я почти заставил его работать, но у меня возникли некоторые проблемы с вспомогательной функцией, которую я написал, чтобы избавиться от пробелов в двух входных словах.Вот код этой функции:

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];
            }
        }
    }
}

Теперь все работает нормально, когда я передаю входное слово из функции main этому помощнику.Проблема заключается во втором вызове этой функции.Когда я вызываю эту функцию на втором входе, если k - это число пробелов на первом входе, то функция стирает первые k буквы второго входа.Например, ввод ./anagram " banana" "banana" даст мне ложный отрицательный результат, и если я добавлю инструкцию print, чтобы увидеть, что происходит с входными данными после вызова noSpaces, я получу следующее:

banana
anana

Вот код для полной программы:

#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;
}

Я знаю, что есть библиотека, которая может работать со строками, но я действительно хотел бы избежать ее использования.Я уже зашел так далеко, не нуждаясь в этом, и я чувствую, что проблема в основном решена, но для одной маленькой вещи я не вижу.

Я пришел из Java, и я новичок в C, если это объясняет, какую ошибку я сделал.

Ответы [ 4 ]

0 голосов
/ 16 февраля 2019

В C строки - это массивы char с нулевым терминатором, то есть байт со значением 0, обычно представляемым как '\0'.Вы не должны принимать какую-либо конкретную длину, такую ​​как 100.Действительно, размер массива в аргументах прототипа функции игнорируется компилятором.Вы можете определить длину, отсканировав нулевой терминатор, что эффективно делает strlen(), или вы можете написать код таким образом, чтобы избежать многократных сканирований, останавливаясь на нулевом терминаторе.Вы должны убедиться, что ваши функции работают для пустой строки, которая является массивом с одним нулевым байтом.Вот проблемы в вашем коде:

В функции noSpaces вы выполняете итерации за концом строки, изменяя память, потенциально принадлежащую следующей строке.Программа имеет неопределенное поведение.

Вы должны остановиться в конце строки.Также используйте 2 индексные переменные для выполнения в линейное время:

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';
}

Вы можете упростить compare, чтобы использовать в третий раз столько тестов в среднем:

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 не определеноповедение для пустой строки, потому что вы читаете char по индексу 1 за концом массива.Вот исправленная версия:

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;
            }
        }
    }
}

Вы должны объявить функции перед использованием или поочередно определить их перед использованием.Ваш код компилируется, потому что компилятор принимает старый стиль C, где прототип для еще невидимых функций был выведен из аргументов, переданных на первом сайте вызова.Эта практика подвержена ошибкам и устарела.

Ваша функция сортировки имеет квадратичную сложность по времени, которая может быть очень медленной для очень длинных строк, но слова не должны быть слишком большими, поэтому это не проблема.

Было бы лучше не изменять строки аргументов.Вы можете выполнить тест с копией одной из строк с той же временной сложностью.

Вот прямой подход:

#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;
}
0 голосов
/ 16 февраля 2019

У вас проблема с переполнением буфера на argv [1] и argv [2], если длина буфера argv [1] меньше 100. Поэтому я думаю, что вам следует использовать цикл с strlen (word), что достаточно.Когда вы используете статическую длину со значением 100 в цикле for, когда-нибудь слово получит данные из другой области памяти и сделает вашу программу неопределенным.И другие функции имеют такую ​​же проблему.Я имею в виду функции sortWords и сравнения .

Вот моя модификация в вашей функции noSpaces, она должна работать.

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];
            }
        }
    }
}
0 голосов
/ 16 февраля 2019

Вместо того, чтобы пытаться удалить пробелы и отсортировать, что составляет O (N lg N) время выполнения.Вы можете выполнить операцию O (N), просто создав массив, который представляет количество каждой буквы в слове.И просто игнорируйте пробелы при этом.

// 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;
}
0 голосов
/ 16 февраля 2019

Вы делаете логическую ошибку в вашей вспомогательной функции.Вы начинаете копирование с word[j], а не с начала второго слова, поэтому вы собираетесь удалить столько начальных символов, сколько начальных пробелов, как вы видите в выводе.

Обратите внимание, что j=i и i подсчитывает количество начальных пробелов из внешнего цикла.

Кстати, у вас должно быть только два цикла.Поместите условие while в первый цикл for следующим образом: for (int i = 0; i<100 && word[i]==' '; i++).

Чтобы исправить логическую ошибку, вам нужно использовать другой итератор k, инициализированный в ноль в самом внутреннем цикле, ииспользуйте word[k] = word[j+1].Я думаю, что это сработает.

...