Как бы вы улучшили этот алгоритм? (смена строки) - PullRequest
6 голосов
/ 20 октября 2008

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

char * reverse(const char * str)
{
  int length = strlen(str);
  char * reversed_string = new char[length+1];

  for(int i = 0; i < length; ++i)
  {
    reversed_string[i] = str[(length-1) - i];
  }
  //need to null terminate the string
  reversed_string[length] = '\0';

  return reversed_string;

}

int main(int argc, char * argv[])
{

  char * rev_str = reverse("Testing");

  cout << "Your string reversed is this: " << rev_str << endl;

  delete rev_str;
  rev_str = 0;

  return 0;
}

Ответы [ 20 ]

16 голосов
/ 20 октября 2008

std::reverse из <algorithm> работает для строк и char массивов:

string str = "Hello";
char chx[] = "Hello";

reverse(str.begin(), str.end());
reverse(chx, chx + strlen(chx));

cout << str << endl;
cout << chx << endl;

/ EDIT: это, конечно, изменяет исходную строку. Но STL на помощь. Следующее создает новую перевернутую строку. К сожалению (?), Это не работает напрямую с массивами C char без создания дополнительной (неявной) копии:

string reverse_string(string const& old) {
    return string(old.rbegin(), old.rend());
}

cout << reverse_string("Hello") << endl;
14 голосов
/ 20 октября 2008

У меня был этот вопрос один раз. Это первый ответ, который приходит на ум, но последующим является следующее: «Теперь сделайте это без выделения памяти».

int length = strlen(string);
for(int i = 0; i < length/2; i++) {
  char c = string[i];
  string[i] = string[length - i];
  string[length - i] = c;
}

РЕДАКТИРОВАТЬ: Некоторые люди выразили презрение к не использовать указатели. Это немного более читабельно, хотя и не совсем оптимально. Другие ввели указатель решения, поэтому я не буду повторять его здесь.

Один комментатор призвал, чтобы это было выполнимо без (на основе стека) ячейки для свопа. Механизм для этого - побитовый XOR. Заменить внутреннюю часть петли на

string[i] = string[i] ^ string[length - i];
string[length - i] = string[i] ^ string[length - i];
string[i] = string[i] ^ string[length - i];

Но в целом современные компиляторы могут оптимизировать локальную переменную наивного свопа. Подробнее См. Википедия

7 голосов
/ 20 октября 2008
if( string[0] )
{
    char *end = string + strlen(string)-1;
    while( start < end )
    {
        char temp = *string;
        *string++ = *end;
        *end-- = temp;
    }
}
3 голосов
/ 20 октября 2008

Ваш код прост и не удивителен. Несколько вещей:

  1. Используйте size_t вместо int для индекса вашего цикла
  2. Хотя ваш компилятор, скорее всего, достаточно умен, чтобы понять, что (длина -1) инвариантен, он, вероятно, недостаточно умен, чтобы понять, что (длина-1) -i лучше всего заменить другой переменной цикла, которая уменьшена в каждом проходе
  3. Я бы использовал указатели вместо синтаксиса массива - мне будет понятнее иметь * dst-- = * src ++; в цикле.

Другими словами:

char *dst = reversed_string + length;
*dst-- = '\0';
while (*src) {
   *dst-- = *src++;
}
3 голосов
/ 21 октября 2008

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

Это пример того, как заставить его работать с GCC.

/* 
 * reverse.c
 *
 * $20081020 23:33 fernando DOT miguelez AT gmail DOT com$
 */

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MAX_CHARS 10 * 1024 * 1024

/*
 * Borrowed from http://coding.derkeiler.com/Archive/Assembler/comp.lang.asm.x86/2007-03/msg00004.html
 * GNU Compiler syntax
 */
inline uint32_t bswap(uint32_t val)
{
    __asm__("bswap %0" : "=r" (val) : "0" (val));
    return val;
}

char * reverseAsm(const char * str)
{
    int i;
    int length = strlen(str);
    int dwordLength = length/4;

    if(length % 4 != 0)
    {
        printf("Error: Input string length must be multiple of 4: %d\n", length);       
        return NULL;
    }

    char * reversed_string = (char *) malloc(length+1);
    for(i = 0; i < dwordLength; i++)
    {
        *(((uint32_t *) reversed_string) + dwordLength - i - 1) = bswap(*(((uint32_t *) str) + i));
    }

    reversed_string[length] = '\0';

    return reversed_string;
}

char * reverse(const char * str)
{
    int i;
    int length = strlen(str);
    char * reversed_string = (char *) malloc(length+1);

    for(i = 0; i < length; ++i)
    {
        reversed_string[i] = str[(length-1) - i];
    }

        //need to null terminate the string

    reversed_string[length] = '\0';

    return reversed_string;
}

int main(void)
{
    int i;
    char *reversed_str, *reversed_str2;
    clock_t start, total;
    char *str = (char *) malloc(MAX_CHARS+1);

    str[MAX_CHARS] = '\0';

    srand(time(0));

    for(i = 0; i < MAX_CHARS; i++)
    {
        str[i] = 'A' + rand() % 26;     
    }

    start = clock();
    reversed_str = reverse(str);
    total = clock() - start;
    if(reversed_str != NULL)
    {
        printf("Total clock ticks to reverse %d chars with pure C method: %d\n", MAX_CHARS, total); 
        free(reversed_str);
    }
    start = clock();
    reversed_str2 = reverseAsm(str);
    total = clock() - start;
    if(reversed_str2 != NULL)
    {
        printf("Total clock ticks to reverse %d chars with ASM+C method: %d\n", MAX_CHARS, total); 
        free(reversed_str2);
    }

    free(str);

    return 0;
}

Результаты на моем старом компьютере под Cygwin:

fer@fernando /cygdrive/c/tmp$ ./reverse.exe
Total clock ticks to reverse 10485760 chars with pure C method: 221
Total clock ticks to reverse 10485760 chars with ASM+C method: 140
3 голосов
/ 20 октября 2008

Мм? Никто не делал это с указателями?

char *reverse(const char *s) {
    size_t n = strlen(s);
    char *dest = new char[n + 1];
    char *d = (dest + n - 1);

    dest[n] = 0;
    while (*s) {
        *d-- = *s++
    }

    return dest;
}

Надеюсь, годы Java не испортили мой C; -)

Редактировать : заменены все эти вызовы strlen на дополнительную переменную. Что возвращает Стрлен в эти дни? (Спасибо Плинтус ).

2 голосов
/ 09 октября 2009

Вы не можете (не должны) делать это:

string[i] ^= string[length - i] ^= string[i] ^= string[length - i];

От: http://en.wikipedia.org/wiki/XOR_swap_algorithm#Code_example

  • * "Этот код имеет неопределенное поведение, так как он дважды изменяет значение l x без промежуточной точки последовательности.
2 голосов
/ 21 октября 2008

@ Конрад Рудольф: (извините, у меня нет "опыта", чтобы оставить комментарий)

Хочу отметить, что STL предоставляет алгоритм reverse_copy () , аналогичный reverse () . Вам не нужно вводить временное так, как вы это сделали, просто выделите новый символ * правильного размера.

1 голос
/ 21 октября 2008

WRT: «Теперь сделайте это без временного хранения переменной» ... Возможно, что-то вроде этого (и пока ведется индексация массива):

int length = strlen(string);
for(int i = 0; i < length/2; i++) {
  string[i] ^= string[length - i];
  string[length - i] ^= string[i];
  string[i] ^= string[length - i];
}
1 голос
/ 20 октября 2008

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

...