Удаление повторяющихся символов из двух строк аргумента в C - PullRequest
0 голосов
/ 17 сентября 2018

Я пытаюсь оптимизировать проблему, чтобы сделать ее более удобочитаемой при той же оптимизации скорости.Моя проблема заключается в следующем:

Разрешенная функция: write.c , больше ничего.

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

Отображение будет в том порядке, в котором символы появляются в командной строке, и за ним последует \ n.

Каквы можете видеть, что в основном он принимает две из ваших строк аргументов (argv[1] и argv[2]) в нашу функцию (void remove_dup(char *str, char *str2) после компиляции с GCC. Этот временный массив будет содержать значение ASCII символа послеобнаружен дубликат. Например, str1 = "hello" и str2 = "laoblc". Ожидаемый вывод будет выглядеть как «heloabc» с использованием функции записи.

Однако GCC жаловался, потому что у меня есть индекс массива с моим временныммассив символов, заполненный нулями из индекса моих строк. Чтобы прекратить жаловаться компилятору, мне пришлось преобразовать строковый индекс как int, чтобы сохранить значение ASCII в моем темпемассив рари.Это будет наша проверка, которая определит, есть ли дубликат в нашей строке в зависимости от значения символа.Перекомпилируйте его снова, но на этот раз с помощью предупреждающих флагов: gcc -Wextra -Werror -Wall remove_dup.c.Это ошибка, которую я получаю:

remove_dup: 11 error: индекс массива имеет тип 'char' [-Werror, -Wchar-subscripts]

           if (temp[str[i]] == 0)
                     ^~~~~~~

remove_dup:13 ошибка: индекс массива имеет тип 'char' [-Werror, -Wchar-subscripts]

                   temp[str[i]] = 1;
                        ^~~~~~~

remove_dup: 21 ошибка: индекс массива имеет тип 'char' [-Werror, -Wchar-subscripts]

           if (temp[str2[i]]  == 0)
                   ^~~~~~~~

remove_dup.c: 23 ошибка: индекс массива имеет тип 'char' [-Werror, -Wchar-subscripts]

                  temp[str2[i]] = 1;
                      ^~~~~~~~

Теперь мой реальный вопросв том, как я могу иметь такую ​​же эффективность по времени, НО без использования какого-либо приведения в мой массив?Эта программа работает как O(m + n), где m - наша первая строка, а n - наша вторая строка.

Это код:

void    remove_dup(char *str, char *str2)
{
    int temp[10000] = {0};
    int i;

    i = 0;
    while (str[i])
    {
        if (temp[(int)str[i]] == 0)
        {
            temp[(int)str[i]] = 1;
            write(1, &str[i], 1);
        }
        i++;
    }
    i = 0;
    while (str2[i])
    {
        if (temp[(int)str2[i]]  == 0)
        {
            temp[(int)str2[i]] = 1;
            write(1, &str2[i], 1);
        }
        i++;
    }
}

int main(int argc, char *argv[])
{
    if (argc == 3)
        remove_dup(argv[1], argv[2]);
    write(1, "\n", 1);
    return (0);
}

Надеюсь, это понятнодостаточно с логической структурой, которую я объяснил.У меня могут быть грамматические ошибки, так что терпите меня:).

Ответы [ 2 ]

0 голосов
/ 17 сентября 2018

Приведение здесь не приведет к снижению производительности.

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

   temp[(int)str[i]]

на:

   temp[+str[i]]

Это будет работать с помощью обычных арифметических преобразований.

Однако ваш код имеетдругая проблема.Вы можете спросить: зачем gcc выдавать такое раздражающее предупреждение?

Один из ответов: им просто нравится раздражать.Лучше предположить, что на большинстве платформ char равен signed - см. Является ли char подписанным или неподписанным по умолчанию? - и так, если ваша строка имеет ASCII-символ, превышающий 127 (т.е.меньше нуля), у вас будет ошибка.

Один из способов исправить это - заменить:

   temp[(int)str[i]]

на:

   temp[str[i] + 128]

(и изменить int temp[10000] = {0} до int temp[256 + 128] = {0}).Это будет работать независимо от значения по умолчанию char.

0 голосов
/ 17 сентября 2018

Теперь мой реальный вопрос: как я могу иметь такую ​​же эффективность по времени, НО без использования какого-либо преобразования в мой массив?

Я не верю, что приведение в C имеет штраф за время выполнения. Все в C все равно число. Я считаю, что это просто говорит компилятору, что да, вы знаете, что используете неправильный тип, и считаете, что это нормально.

Обратите внимание, что char может быть подписано. В него может проникнуть отрицательное число.

Эта программа работает как O (m * n), где m - наша первая строка, а n - наша вторая строка.

Нет, он работает как O (n). O (m * n) было бы, если бы вы перебирали одну строку для каждого символа другого.

for( int i = 0; i < strlen(str1); i++ ) {
    for( int j = 0; j < strlen(str2); j++ ) {
        ...
    }
}

Но вы перебираете каждую строку одну за другой в двух независимых циклах. Это O (m + n), то есть O (n).


На улучшениях. Во-первых, temp нужно только удерживать диапазон char, который, самое большее, 256. Давайте дадим ему имя переменной, которая описывает, что она делает, chars_seen.

Наконец, нет необходимости хранить полное целое число. Обычно мы использовали бы bool из stdbool.h, но мы можем определить наше собственное, используя signed char, что, вероятно, и сделает stdbool.h. Мы обязательно завернем его в #ifndef bool, поэтому мы будем использовать поставляемую систему, если она доступна, она будет знать лучше, чем мы, какой тип использовать для логического значения.

#ifndef bool
  typedef signed char bool;
#endif
bool chars_seen[256] = {0};

Возможно, вам удастся повысить производительность, исключив i и вместо этого непосредственно увеличивая указатель. Это не только повышает производительность, но и упрощает многие операции со строками и массивами.

for( ; *str != '\0'; str++ ) {
    if( !chars_seen[(size_t)*str] ) {
        chars_seen[(size_t)*str] = 1;
        write(1, str, 1);
    }
}

Обратите внимание, что я преобразую в size_t, а не int, потому что это правильный тип для индекса.

Возможно, вы сможете побриться, используя постинкремент, будет ли эта помощь зависеть от вашего компилятора.

    if( !chars_seen[(size_t)*str]++ ) {
        write(1, str, 1);
    }

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

inline void display_chars_no_dups( const char *str, bool chars_seen[]) {
    for( ; *str != '\0'; str++ ) {
        if( !chars_seen[(size_t)*str]++ ) {
            write(1, str, 1);
        }
    }
}

Затем main выделяет массив видимых символов и вызывает функцию столько раз, сколько необходимо.

int main(int argc, char *argv[]) {
    bool chars_seen[256] = {0};

    for( int i = 1; i < argc; i++ ) {
      display_chars_no_dups( argv[i], chars_seen );
    }
    write(1, "\n", 1);
}
...