как выполнить переворачивание предложения слово за словом в C?

#include <stdio.h>

int main(void)
{
  int i,j;
  int wordstart = -1;
  int wordend = -1;
  char words[]= "this is a test";
  char temp;

  // Reverse each word
  for (i = 0; i < strlen(words); ++i)
  {
    wordstart = -1;
    wordend = -1;
    if(words[i] != ' ') 
      wordstart = i;
    for (j = wordstart; j < strlen(words); ++j)
    {
      if(words[j] == ' ')
      {
        wordend = j - 1;
        break;
      }
    }
    if(wordend == -1)
      wordend = strlen(words);
    for (j = wordstart ; j <= (wordend - wordstart) / 2; ++j)
    {
      temp = words[j];
      words[j] = words[wordend - (j - wordstart)];
      words[wordend - (j - wordstart)] = temp;
    }
    i = wordend;
    printf("reversed string is %s:", words);
  }
}

Я пробовал таким образом, но получаю следующий результат:
siht is a test
мой ожидаемый результат:
test a is this

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


person GEEK max    schedule 09.03.2012    source источник
comment
@MichaelBurr, можешь указать на мою ошибку?   -  person GEEK max    schedule 09.03.2012


Ответы (9)


Возможно, это место на сайте обзора кода?

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

Две возможные ошибки выглядят так:

wordend = strlen(words);

должно быть

wordend = strlen(words)-1;

и

for(j = wordstart ; j <= (wordend - wordstart) / 2 ; ++j) {

должно быть

for(j = wordstart ; j <= (wordend + wordstart) / 2 ; ++j) {

Окончательный код выглядит так (с некоторыми дополнительными {}):

    #include <stdio.h>
    int main(int argc,char *argv[])
    {
        int i,j;
        char words[]= "this is a test";
        int L=strlen(words);

        // Reverse each word
        for(i = 0; i < L; ++i) {
          int wordstart = -1;
          int wordend = -1;
          if(words[i] != ' ') 
          {
            wordstart = i;

            for(j = wordstart; j < L; ++j) {
              if(words[j] == ' ') {
                wordend = j - 1;
                break;
              }
            }
            if(wordend == -1)
              wordend = L-1;
            for(j = wordstart ; j <= (wordend + wordstart) / 2 ; ++j) {
              char temp = words[j];
              words[j] = words[wordend - (j - wordstart)];
              words[wordend - (j - wordstart)] = temp;
            }
            i = wordend;
          }
        }
        printf("reversed string is %s:",words);
        return 0;   
    }
person Peter de Rivaz    schedule 09.03.2012
comment
Можете ли вы сказать мне выполнение программы в командном режиме, почему этот main(int argc,char *argv[]) полезен - person GEEK max; 09.03.2012
comment
Ой, извините, это просто моя привычка. Это никак не поможет вашей программе. - person Peter de Rivaz; 09.03.2012

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

Когда вы дойдете до конца предложения, просто прокрутите список в обратном направлении и печатайте слова по мере их прохождения.

person Luchian Grigore    schedule 09.03.2012
comment
Я новичок в c. Я не знаю применения связанного списка, поэтому можете ли вы указать на ошибку в моей программе или предложить мне другой подход, отличный от связанных списков? - person GEEK max; 09.03.2012
comment
@GEEKmax вы отладили свой код, чтобы увидеть, что с ним не так. Пожалуйста, не начинайте работу со StackOverflow, думая, что мы здесь, чтобы сделать за вас домашнее задание. Мы можем помочь вам с конкретными проблемами. - person Luchian Grigore; 09.03.2012
comment
@Lucian Grigore извините, это не мое намерение, я новичок в C, мне потребуется как минимум час, чтобы отладить это - person GEEK max; 09.03.2012

Просто мы можем просто использовать массив 2D-символов n * 1, адаптированный к нашим потребностям !!!

#include <stdlib.h>

int main()
{
    char s[20][20];
    int i=0, length=-1;
    for(i=0;;i++)
    {
        scanf("%s",s[i]);
        length++;
        if(getchar()=='\n')
            break;
    }
    for(i=length;i>=0;i--)
        printf("%s ",s[i]);
    return 0;
}
person Sriram Jayaraman    schedule 04.08.2016
comment
это самый простой способ ... это здорово. - person taruntejae; 02.12.2016

Начните токенизировать строку с последнего символа и продолжайте до первого символа. Держите один указатель привязанным к основанию текущего слова, а другой — указывающим, который будет уменьшаться, пока не будет найдено начало слова. Когда вы найдете начало слова при сканировании таким образом, печатайте от указателя начала слова до привязки конца слова. Обновите привязку конца слова к предыдущему символу начального символа текущего слова.

Возможно, вы захотите пропустить символы пробела при сканировании.

ОБНОВЛЕНИЕ

Это быстрая реализация:

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

#define MAX_BUF 256

void show_string (char *str, int i, int n)
{
  while (i <= n)
  {
    printf ("%c", str[i]);
    i++;
  }
}

int main (void)
{
  char str[MAX_BUF];
  int end_anchor, start_ptr;
  int state;

  printf ("\nEnter a string: ");
  scanf (" %[^\n]", str);

  start_ptr = strlen (str) - 1;

  end_anchor = start_ptr;
  state = 0;
  while (start_ptr >= -1)
  {
    switch (state)
    {
      case 0:
             if ((!isspace (str[start_ptr]) && (start_ptr >= 0)))
             {
               start_ptr--;
             }
             else
             {
               state = 1;
             }
             break;

      case 1:
             show_string (str, start_ptr + 1, end_anchor);
             state = 2;
             start_ptr--;
             printf (" ");
             break;

      case 2:
             if (!isspace (str[start_ptr]))
             {
               state = 0;
               end_anchor = start_ptr;
             }
             else
             {
               start_ptr--;
             }
             break;
    }
  }


  printf ("\n");
  return 0;
}

end_anchor указывает на каждое конечное слово, а start_ptr находит начало слова, конец которого находится в end_anchor. Когда мы находим начало слова (пробелами или start_ptr = -1), мы печатаем все символы от start_ptr + 1 до end_anchor. + 1 связано с реализацией: start_ptr указывает на символ пробела, а процедура печати напечатает все символы от i до n. Как только мы обнаружили одно пустое пространство, мы печатаем его и пропускаем соседние пробелы (в case 2) и сохраняем только одно, напечатанное вручную. Как только обнаруживается непустое пространство, у нас есть другой конец слова, для которого мы устанавливаем end_anchor на этот индекс в case 2 и устанавливаем state = 0 , чтобы мы могли снова искать начало слова.

person phoxis    schedule 09.03.2012
comment
ты имеешь в виду брать два указателя, один изначально указывал на начало, а другой на конец, я не понял, не могли бы вы сказать это более четко - person GEEK max; 09.03.2012
comment
что делает эта строка scanf ( %[^\n], str); - person GEEK max; 09.03.2012
comment
пропускать пробелы в начале строки и читать до тех пор, пока не будет найден \n, т.е. читает строку, содержащую пробелы. - person phoxis; 09.03.2012
comment
да, есть ли что-то понятное из синтаксиса, потому что это трудно запомнить - person GEEK max; 09.03.2012
comment
вы можете использовать что угодно, например fgets для получения строки или что-то еще, это не главная проблема. - person phoxis; 09.03.2012

if(words[i] != ' ') 
    wordstart = i;

Это утверждение, как насчет части else? если words[i] == ' ', а wordstart остается -1. Так что, возможно, попробуйте использовать:

while (words[i] && words[i] == ' ') ++i;
  if (!words[i])
      break;
wordstart = i;

Затем вы должны вывести результат из цикла i. Наконец, если вы хотите получить ожидаемый результат, вам следует еще раз перевернуть все предложение так, как вы использовали его в цикле.

person Daybreakcx    schedule 09.03.2012
comment
не понял, не могли бы вы сказать мне, где я должен заменить свой код на ваш - person GEEK max; 09.03.2012

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

Я бы написал собственную функцию вместо strrchr, потому что strrchr вычисляет длину заданной строки, что в данном случае избыточно. Эту длину не нужно вычислять более одного раза.

Вот код:

char* findLastWord(char* str, int* len)
{
    int i;
    for (i = *len - 1; i >= 0; --i)
    {
        if (str[i] == ' ')
        {
            str[i] = '\0';
            if (i < *len - 1)
            {
                *len = i - 1;
                return &str[i + 1];
            }
        }
    }
    return NULL;
}

int main (int argc, char *argv[])
{
    char str[] = " one two three  four five six ";
    int len = strlen(str);

    char* lastWord = findLastWord(str, &len);
    while (lastWord != NULL)
    {
        printf("%s\n", lastWord);
        lastWord = findLastWord(str, &len);
    }
    if (len > 1)
        printf("%s\n", str);
    return 0;
}

выход:

six
five
four
three
two
one

Надеюсь это поможет ;)

person LihO    schedule 09.03.2012

Используйте основной цикл for для перехода до конца предложения: копируйте буквы в строке, пока не найдете пробел. теперь вызовите функцию add@beginning и в этой функции добавляйте строку каждый раз, когда вы передаете строку в связанный список. распечатать содержимое связанного списка с пробелом между ними, чтобы получить ожидаемый результат

person sandy    schedule 22.06.2016
comment
Ваш ответ может быть полезным из некоторого примера исходного кода. - person Patrick Trentin; 22.06.2016

Мой код, просто перейдите от последнего, и если вы найдете пробел, напечатайте символы перед ним, теперь измените конец на пробел-1; это будет печатать до второго слова, наконец, просто напечатать первое слово, используя один цикл for. Комментарий для альтернативного подхода.

Программа:

#include<stdio.h>
int main()
{
 char str[200];
int i,j,k;
scanf("%[^\n]s",&str);
for(i=0;str[i]!='\0';i++);
i=i-1;
for(j=i;j>=0;j--)
{
    if((str[j])==' ')
    {
        for(k=j+1;k<=i;k++)
        {
            printf("%c",str[k]);
        }
        i=j-1;
        printf(" ");
    }

}
for(k=0;k<=i;k++)
{
    printf("%c",str[k]);
}
}
person joness    schedule 01.07.2016

person    schedule
comment
Приведенный выше код переворачивает предложение, используя char *r и печатая cout‹‹r внутри функции sentrev(). Но я хочу хранить в *r. Пожалуйста, изложите свою идею. Спасибо - person kriya; 26.02.2014