6

I'm currently going through K.N. King's C Programming: A Modern Approach. I've made it past the text for the 8th chapter (Arrays), and I'm eager to move on to chapter 9, but I've yet to solve the so-called "programming projects" at the end of each chapter. Unfortunately, the 14th... bugs me.

Write a program that reverses the words in a sentence.

Enter a sentence: you can cage a swallow can't you?
Reversal of sentence: you can't swallow a cage can you?

Hint: Use a loop to read the characters one by one and store them in a one-dimensional char array. Have the loop stop at a period, question mark, or exclamation point (the "terminating character "), which is saved in a separate char variable. Then use a second loop to search backward through the array for the beginning of the last word. Print the last word, then search backward for the next-to-last word. Repeat until the beginning of the array is reached. Finally, print the terminating character.

I've been thinking of defining a word as a sequence of characters between blank spaces. So when a space is reached, go backward, printing each character, until another space is found. My first version of the program only printed the first word. The current version of it only prints the other words. I've been stuck on this for two days, so any help is truly appreciated. Here is my code, as well as an output sample. Hopefully I've properly documented my code. Thanks in advance!

Code

/* Include the standard I/O library */
#include<stdio.h>

/* Define main */
int main(void) {

    /**
     * Declare an array of characters storing the sentence, as well as
     * a character representing the current character under cursor and
     * the terminating character
     */
    char sentence[100] = { ' ' }, c, tc;

    /**
     * Declare a loop counter already initialized at 0, an incremental
     * variable, as well as the size of the read sentence
     */
    int i = 0, j = 1, size = 0;

    /* Get the sentence */
    printf("Enter a sentence: \n");
    for(c = getchar(); (c != '.') && (c != '!') && 
        (c != '?') && (c != '\n'); c = getchar(), i++) {

        sentence[i] = c; /* Store the current character in the array */
        size++; /* Increase the sentence's size */
    }

    tc = c; /* Get the terminating character */

    /**
     * Go backward through the array, printing each sequence of characters
     * between spaces
     */
    for(i = 99; i >= 0; i--) {

        if(sentence[i] == ' ') {

            while(sentence[i + j] != ' ') {

                printf("%c", sentence[i + j]);
                j++;
            }

            j = 1; /* Reset the incremental variable */
            printf(" "); /* Print a tailing space */
        }
    }

    /**
     * Delete the tailing blank space and print the terminating character,
     * as well as a new line 
     */
    printf("\b%c\n", tc);

    return 0; /* Return 0 upon successful program execution */
}

Output:

http://drp.ly/1nYt5J

XLR3204S
  • 199
  • 1
  • 1
  • 9
  • There is a much easier solution, IMHO. First reverse each word in place, and then reverse the entire string. – Carl Norum Jul 18 '10 at 18:00
  • @Carl Norum my problem seems to be related to delimiting words. How exactly would I more easily reverse each word? Thanks! – XLR3204S Jul 18 '10 at 18:08
  • I'm writing up a quick example now. Hold on one moment! – Carl Norum Jul 18 '10 at 18:09
  • related: [Given an array of characters which form a sentence of words, give an efficient algorithm to reverse the order of the words (not characters) in it](http://stackoverflow.com/q/47402/4279) – jfs Dec 09 '13 at 02:23

10 Answers10

5

push each word on a stack and read the stack from index 0 to N-1

Quonux
  • 2,975
  • 1
  • 24
  • 32
  • this is hard to do in C if this is his first time messing with strings in C. we're not in C++ or perl land here, toto. :-) – eruciform Jul 18 '10 at 18:34
  • I've edited my post removing the "strings" tag. I think many misunderstandings have come across. Apart from this not being a "homework" really, it's also not related to strings. So far, I'm *supposed* to know only Loops, Conditionals, console I/O, basic data types, and, now, arrays. No strings or functions. So I think I have to come up with an ad-hoc solution. Stacks are... two chapters away. – XLR3204S Jul 18 '10 at 18:42
3

Another methodology to think about:

you can cage a swallow can't you?
uoy t'nac wollaws a egac nac uoy?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
you t'nac wollaws a egac nac uoy?
^^^
you can't wollaws a egac nac uoy?
    ^^^^^
you can't swallow a egac nac uoy?
          ^^^^^^^
you can't swallow a egac nac uoy?
                  ^
you can't swallow a cage nac uoy?
                    ^^^^
you can't swallow a cage can uoy?
                         ^^^
you can't swallow a cage can you?
                             ^^^

For each thing you want to reverse (be it a whole sentence or a word):

  1. Find the beginning and end
  2. Swap the beginning and end characters
  3. Move "inwards" once
  4. keep going until you reach "the middle"

Since reversing a chunk of a string is a common operation, it makes sense to make it its own function. And since the only information the function need to do its job is:

  1. the string
  2. the beginning index
  3. the ending index

What do you think the parameters for the function would be?

The other common thing that needs to be done over and over is "finding" something, be it a space or a punctuation mark. You may need to write this yourself, or if you can use library functions, or want a hint, look up:

man strcspn
eruciform
  • 7,680
  • 1
  • 35
  • 47
  • Thing is... I'm not new to programming, I've done extensive work in PHP (and the related web technologies). But two weeks ago I wanted to pick C, this time for good (yes, I do have past attempts, but school was taking way too much time, so I had to give up). So I bought this book and I'm trying to follow it thoroughly. There have been many places where I could have used things like... functions, but I didn't want to, because, as I said, that's the subject of the next chapter, and I'm not trying to rush things. I'm gonna try making the most out of your suggestions. Thanks! – XLR3204S Jul 18 '10 at 18:24
  • no problem, glad to help! this is a bit of a large first project messing with strings in c. each of the sub-problems i mentioned (which should go into a separate function each) would be a good place to start. write a program to find either a `'.'` or a `'?'` in a string, and figure out the index. write a program to reverse all the of the characters in a string, all the way to the end (not the `'\0'` though!). and write a program that finds and prints out the individual words in a sentence. if you can do those three things separately, you can add them together to solve this larger problem. :-) – eruciform Jul 18 '10 at 18:41
  • I was thinking of writing the first function you suggest, as it would be way easier to look for the end of the input string by using a function with a variable number of arguments. But I'm not supposed to know how to write a function, so, as I said, I'll have to come up with an ad-hoc solution. As for the second function, do I need two arrays, or am I missing something? Finally, is my idea of defining words as sequences of characters delimited by spaces a good one? – XLR3204S Jul 18 '10 at 18:48
  • 1
    you don't need a variable number of arguments. check out `strcspn`. the first arg is the string to search, the second is a string containing several characters, any of which will trigger the find. `strtok` and several others work this way as well. stay away from variable-arg functions in c unless you really, really need to. for the second function, you can do it in-place. think of juggling. you can swap two balls if you keep one in the air. just make room for that one ball, do the swap, and move on to the next swap. and yes, space-separated is easiest, if not 100% correct - start there. – eruciform Jul 18 '10 at 18:55
2

Here's an example that does what I mentioned. First, reverse each word in place, then reverse the entire string. Here is a reverse() function that reverses a string in place with a given delimiting character. You could extend to use multiple delimiters if you like.

char *reverse(char *str, char delim)
{
  char *end = strchr(str, delim);
  char *ret;
  char tmp;

  if (end == NULL)
    end = strchr(str, '\0');

  ret = end + 1;
  end--;

  while (end > str)
  {
    tmp = *str;
    *str = *end;
    *end = tmp;

    end--;
    str++;
  }

  return ret;
}

Here is a use case with a little example program:

int main(int argc, char **argv)
{
  char *end = strchr(argv[1], '\0');
  char *str = argv[1];

  while (str < end)
    str = reverse(str, ' ');

  reverse(argv[1], '\0');
  printf("%s\n", argv[1]);

  return 0;
}

Usage example:

$ ./example "the quick red fox jumps over the lazy brown dog"
dog brown lazy the over jumps fox red quick the
Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • 1
    `-1` for giving a full-blown solution to someone who needs a nudge into the right direction. How is XLR3204S to learn to think when you give a ready-made solution to paste?! – sbi Jul 18 '10 at 18:26
  • 1
    @sbi, he says he's not new to programming - only new to C. The point is to help him with a well written example in the language he's trying to learn. Algorithms is not the problem here, learning a specific language syntax is. When *I'm* learning something, I usually want to see a good example - a lot more than I want some vague hints about correct syntax. – Carl Norum Jul 18 '10 at 18:30
  • 1
    Who is pasting anything? I'm free to skip this problem and the next N chapters if I want to. I'm trying to make the most out of his response/program, by looking up the parts I need. I think the *homework* tag is luring people into thinking I'm doing this for a class or something. – XLR3204S Jul 18 '10 at 18:31
  • The tags were edited and this one was added by eruciform. I thought it was basic procedure, I see other self-taughts have the *homework* tag. Can I remove it without being sanctioned for it? – XLR3204S Jul 18 '10 at 18:35
  • my fault, it really looked like a homework problem. my apologies. – eruciform Jul 18 '10 at 18:47
  • @Carl: There's an exercise @XLR3204S wants to do, but gets stuck and asks for help. You post a pasteable solution. I'm sorry if this offends you, but, having taught programming for years, that's a pattern I passionately dislike and which I down-vote. (If there was `homework` tag, I didn't see it, BTW.) – sbi Jul 18 '10 at 19:30
1
int main()
{

    char sent[50],last,s;
    int i,j,length,k,temp,b;
    clrscr();
    i=0;
    printf("Enter a sentence: ");
    sent[i]=getchar();
    while(sent[i]!='\n'&&sent[i]!='.'&&sent[i]!='?'&&sent[i]!='!')
    {
        sent[++i]=getchar();
    }
    last=sent[i];//storing last char
    b=i; //length of string
    printf("Reverse of sentence: ");
    for(;;)
    {
        k=b-1;// begin from last position
        temp=k;
        while(sent[k]!=' ' && k!=-1)
        k--;
        s=k;//storing space here
        b=s;
        for(j=b+1;j<=temp;j++)
        putchar(sent[j]);
        if(s!=-1)
        putchar(sent[s]);
        if(b==-1)
        break;
    }
    putchar(last);
    getch();
    return 0;
}
CoffeeRain
  • 4,460
  • 4
  • 31
  • 50
1

By taking the input as character array and then reversing the whole array. Following this, reverse word by word where splitting of the sentence into words occur at " ","?","\0" etc. Hope this helps.

 void reverse(char s[],int start,int stop){
 char t;
 while(start<stop){
    t = s[start];
    s[start]=s[stop];
    s[stop]=t;
    start++;
    stop--;
}
}

int main() {

char str[100];
gets(str);

int pos=0,begin=0,end;
reverse(str,begin,strlen(str)-1); //since the last character is null

while(pos<=strlen(str)){
    if((str[pos]==' ')||(str[pos]=='\0')||(str[pos]=='?')||(str[pos]=='!')){
        end = pos - 1; 
       reverse(str,begin,end);  
        begin = pos+1; //for the next word
    }

    pos++;

}   

cout<<str;
return 0;
}
0

Here is my answer

/* write a program that reverses the words in a sentence:

Enter a sentence: you can cage a swallow can't you?

Reversal of sentence: you can't swallow a cage can you?

Hint: Use a loop to read the characters one by one and store them in a one-dimensional char array.

Have the loop stop at a period, question mark, or exclamation point -- (the "terminating character"), which is saved as a separate char variable.

Then use a second loop to search backward through the array for the beginning of the last word.

Print the last word, then search backward for the next to last word. Repeat until the beginning of the array is finally reached.

Finally print the terminating character.

*/
#include<stdio.h>

int main()
{
int ch;
char sentence[200]; //hard set a limit of 200 character sentence
char word[10] = {'\0','\0','\0','\0','\0','\0','\0','\0','\0'}; //hard set limit of 10 character words
int i = 0; //character position in input
int w = 9; //character position in word
char terminator = '\0';
printf("Enter a sentence:");
   while  ( (ch=getchar()) != '\n' )
       {
       if ( ch == '.' || ch == '?' || ch == '!')
          terminator = ch;
       else
         {
         sentence[i] = ch;
         i++;
         }
//       printf("%d",i);
       }
       sentence[i] = '\0';//set last character to null


    int x;
    for ( x=i ; x >= 0 ; x-- )
        {
           if ( sentence[x] == ' ' )
           {
            printf(" ");//print the space followed by what is in the word buffer/array
//          printf("word length %d ",w);
            printf("%c",word[0]); //probably should have a for loop here
            printf("%c",word[1]);
            printf("%c",word[2]);
            printf("%c",word[3]);
            printf("%c",word[4]);
            printf("%c",word[5]);
            printf("%c",word[6]);
            printf("%c",word[7]);
            printf("%c",word[8]);
            printf("%c",word[9]);
            w = 9 ;
            word[0] = '\0'; //fill the word buffer/array with null
            word[1] = '\0';
            word[2] = '\0';
            word[3] = '\0';
            word[4] = '\0';
            word[5] = '\0';
            word[6] = '\0';
            word[7] = '\0';
            word[8] = '\0';
            word[9] = '\0';
//          printf("\n");
//          printf("sentence position %d ",x);
           }
           else //assign the letters from sentence[] to letters in word[]
           {
            word[w] = sentence[x];
            w--;
//          printf("word length %d ",w);
//          printf("%c",sentence[x]);
           }
         }
//print the first word because im using space to delimit the words unless i have a space at the
//beginning of the sentence the code above will skip the first word inputed
    printf(" ");//print the space followed by what is in the word buffer/array
    printf("%c",word[0]);
    printf("%c",word[1]);
    printf("%c",word[2]);
    printf("%c",word[3]);
    printf("%c",word[4]);
    printf("%c",word[5]);
    printf("%c",word[6]);
    printf("%c",word[7]);
    printf("%c",word[8]);
    printf("%c",word[9]);



if ( terminator != '\0' ) //prints a . ? or ! if it is including in the inputed sentence
    printf("%c",terminator);

    printf("\n");
    printf("\n");
return 0;
seamus murray
  • 57
  • 1
  • 5
0

Reverse words in a string (words are separated by one or more spaces) - this problem can be handled various ways, few of the solutions i have seen so far use extra memory. The idea is to get the optimum solution, like without using extra memory (in place) solution with time complexity O(N).

So Let's take the example, lets say string is "Hello World" - and expected O/P "World Hello"

  • First we will just reverse the entire sentence IN PLACE, like "dlroW olleH" (We have used XOR operation for swapping. please have a look at Swap() method)
  • Now we increase the index and stop when we come across ' '(space).
  • Once we encounter any space, we know that we got a word. Let's invoke the reverse function on that word and Reverse that word. So in that case it will be like, "World olleH"
  • Now we go further and stop when our index reach till the length of the sentence.
  • Once we reach at end, grab the last index -1 and reverse the last word, so it will be like "World Hello"

One important point to note here, when we invoke the Reverse Function to reverse the word, we will be required to provide the starting index and the ending index of that particular word in respective sentence.

The sample code would look like as mentioned below. I haven't tested with edge cases - but it will provide basic idea of approach.

  using System;

  namespace SampleString
  {
    class ReverseWordsInSetence
    {
        // Reverse words in a string (words are separated by one or more spaces).
        private static String GetReverseWordsInSetence(string sentence)
        {
            char[] stringArray = sentence.ToCharArray();
            int len = sentence.Length;
            int startIndex = 0;

            Swap(ref stringArray, ref startIndex , len-1);
            startIndex = 0;

            for (int currentIndex = 0; currentIndex < len; currentIndex++)
            { 
                if (stringArray[currentIndex].Equals(' '))
                {
                    Swap(ref stringArray, ref startIndex, currentIndex-1);
                }
                else if (currentIndex == len - 1)
                {
                    Swap(ref stringArray, ref startIndex, currentIndex);
                }

            }

            return new string(stringArray);
        }


        private static void Swap(ref char[] a, ref int i, int j)
        {
            int tempIndex = j;

            while (i < j)
            {
                if (a[j].Equals('.'))
                {
                    j--;
                }
                else
                {
                    a[i] ^= a[j];
                    a[j] ^= a[i];
                    a[i++] ^= a[j--];
                }
            }

            i = tempIndex + 2;
        }

        static void Main(string[] args)
        {        
           Console.WriteLine(GetReverseWordsInSetence("Hello World."));
           Console.ReadLine();
        }
    }
}

0

I havent tried it. hope would be helpful to u.

char temp[100];
int j=0, k=100, l=0;
for(i=size-1; i>=0; i--){
  if(sentence[i] == ' ' || i == 0){
    if(k-i >= 2){// at least one character

      if(i==0) j = 0;
      else j = i+1;

      for( l=0; j < k; j++, l++){
         temp[l] = sentence[j];
      }
      temp[l] = '\0';
      printf("%s ",temp);
    }
    k = i;
  }
}
printf("\b%c",tc);
Sadat
  • 3,493
  • 2
  • 30
  • 45
  • Maybe because it combines parts of your code with parts of my code, but I did not downvote your answer, someone else did. I'll try upvoting to counter it... – XLR3204S Jul 18 '10 at 18:38
  • thanks. i have followed your pattern, so that you could get it easily. I have told already, i have typed it here, not tested- but logic should work well. – Sadat Jul 18 '10 at 18:41
  • Sadat: I down-voted your answer for the same reason I down-voted Carl's. I'm sorry if you didn't understand this, I thought it was obvious. – sbi Jul 18 '10 at 19:32
0

The following code pushes the words on a stack and then reads out the stack backwards, as Quonux hinted at.

#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;
}
Community
  • 1
  • 1
Sriram Jayaraman
  • 800
  • 1
  • 8
  • 15
  • Please include some explanation for future visitors. – tmthydvnprt Aug 04 '16 at 13:50
  • While this code may answer the question, providing additional context regarding *how* and/or *why* it solves the problem would improve the answer's long-term value. - [From Review](http://stackoverflow.com/review/low-quality-posts/13226706) – Michael Parker Aug 04 '16 at 14:22
0

The answers so far have contributed alternative algorithms, which can be sorted into two classes:

  1. Reverse the entire sentence and reverse all the words in it. Some versions first reverse the words while others first reverse the sentence; the order in which these reversals are applied does not matter. The net effect is that the words appear in reverse order but the characters within each word are in their normal order.
  2. Put the words on a stack, then take them from the stack again so that the last word becomes the first.

Rather than suggesting yet another alternative algorithm (which would probably fall in one of the above two categories), I will explain why the code in the original question does not work as intended.

First, observe that the code in the question is actually a variant of class 1. It first reverses the entire sentence and then reverses each word:

/* Outer loop goes backwards through the array, effectively reversing the sentence */
for(i = 99; i >= 0; i--) {

    if(sentence[i] == ' ') {

        /* Inner loop goes forward, reversing the word again */
        while(sentence[i + j] != ' ') {

            printf("%c", sentence[i + j]);
            j++;
        }

        j = 1;
        printf(" ");
    }
}

Apart from some beginner mistakes, this is actually an optimal way to reverse the words of a sentence. It uses no additional memory and it does not waste time.

The asker noted that the algorithm works as intended, except that it does not print the first word of the original sentence (which should become the last word). The reason for this is that the array traversals stop on ' ' in both directions. When the outer loop reaches the start of the sentence, it finds no space, because the first character of the user input overwrites the space in sentence[0]:

/* ... */
char sentence[100] = { ' ' }, c, tc;

/* ... */
int i = 0, j = 1, size = 0;

/* Get the sentence */
printf("Enter a sentence: \n");
for(c = getchar(); (c != '.') && (c != '!') && 
    (c != '?') && (c != '\n'); c = getchar(), i++) {

    sentence[i] = c; /* Store the current character in the array */
    size++; /* Increase the sentence's size */
}

Hence, when i becomes 0 in the outer loop, there is no space, and the inner loop which should print the word starting at sentence[0] is never entered. i then decrements to -1 and the outer loop terminates.

You can test this without changing the code by simply running the program as a user. If you enter a space as the first character, the response of the program will be correct:

Enter a sentence:
 you can cage a swallow can't you?
you can't swallow a cage can you?

There are two ways in which you can enforce the inclusion of the first word in your code. The first is to simply always put a space at the beginning of your sentence array. You can do that by starting to copy the user input at i = 1 instead of i = 0:

/**
 * Declare a loop counter already initialized at 1, an incremental
 * variable, as well as the size of the read sentence
 */
int i = 1, j = 1, size = 0;

Another, slightly less elegant way would be to simply repeat the inner loop after the outer loop terminates:

/**
 * Go backward through the array, printing each sequence of characters
 * between spaces
 */
for(i = 99; i >= 0; i--) {

    if(sentence[i] == ' ') {

        while(sentence[i + j] != ' ') {

            printf("%c", sentence[i + j]);
            j++;
        }

        j = 1; /* Reset the incremental variable */
        printf(" "); /* Print a tailing space */
    }
}

/* print the last word */
while(sentence[i + j] != ' ') {

    printf("%c", sentence[i + j]);
    j++;
}

You can make that less repetitive by factoring out the inner loop to a new function. Here's the entire algorithm with inner loop factored out to the print_word function, skipping the comments and blank lines:

#include<stdio.h>

void print_word(char[] sentence, int i) {
    int j = 1;
    while(sentence[i + j] != ' ') {
        printf("%c", sentence[i + j]);
        j++;
    }
}

int main(void) {
    char sentence[100] = { ' ' }, c, tc;
    int i = 0, j = 1, size = 0;
    printf("Enter a sentence: \n");
    for(c = getchar(); (c != '.') && (c != '!') && 
        (c != '?') && (c != '\n'); c = getchar(), i++) {
        sentence[i] = c; /* Store the current character in the array */
        size++; /* Increase the sentence's size */
    }
    tc = c; /* Get the terminating character */
    for(i = 99; i >= 0; i--) {
        if(sentence[i] == ' ') {
            print_word(sentence, i);
            printf(" "); /* Print a tailing space */
        }
    }
    print_word(sentence, i);
    printf("\b%c\n", tc);
    return 0; /* Return 0 upon successful program execution */
}

As a final remark, there's one more thing that you could do better. Right now, the outer loop starts at i = 99, the last possible character in the sentence array. However, while reading the user input you updated i to point to the next input position, so just before the start of the outer loop, i is already pointing to the first character after the sentence. Why not use that and simply start at i - 1?

Julian
  • 4,176
  • 19
  • 40