15

Given an array of characters which forms a sentence of words, give an efficient algorithm to reverse the order of the words (not characters) in it.

Example input and output:

>>> reverse_words("this is a string")
'string a is this'

It should be O(N) time and O(1) space (split() and pushing on / popping off the stack are not allowed).

The puzzle is taken from here.

Bernhard Barker
  • 54,589
  • 14
  • 104
  • 138
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • Does this answer your question? [Reverse the ordering of words in a string](https://stackoverflow.com/questions/1009160/reverse-the-ordering-of-words-in-a-string) – Peter Duniho May 08 '21 at 19:46

21 Answers21

34

A solution in C/C++:

void swap(char* str, int i, int j){
    char t = str[i];
    str[i] = str[j];
    str[j] = t;
}

void reverse_string(char* str, int length){
    for(int i=0; i<length/2; i++){
        swap(str, i, length-i-1);
    }
}
void reverse_words(char* str){
    int l = strlen(str);
    //Reverse string
    reverse_string(str,strlen(str));
    int p=0;
    //Find word boundaries and reverse word by word
    for(int i=0; i<l; i++){
        if(str[i] == ' '){
            reverse_string(&str[p], i-p);
            p=i+1;
        }
    }
    //Finally reverse the last word.
    reverse_string(&str[p], l-p);
}

This should be O(n) in time and O(1) in space.

Edit: Cleaned it up a bit.

The first pass over the string is obviously O(n/2) = O(n). The second pass is O(n + combined length of all words / 2) = O(n + n/2) = O(n), which makes this an O(n) algorithm.

SamB
  • 9,039
  • 5
  • 49
  • 56
Thomas Watnedal
  • 4,903
  • 4
  • 24
  • 23
  • hi, i was curious if the complexity of second pass is n+all words/2, coz for every pass in the second time you are doing a lenght/2 computations, so it shud be word length * wordlength/2 + so on....correct me if i am wrong.. – sriks Mar 17 '11 at 23:54
  • @sriks: In this case you can not multiply the complexity of the outer for with the inner work. By using [Amortized_analysis](http://en.wikipedia.org/wiki/Amortized_analysis) you can see that the total cost of the entire reverse_words operation is linear. Note that the reverse_string operations in the for loop is executed for each word and has complexity linear to the word length. That will never be greater that the total string length. – Thomas Watnedal Mar 24 '11 at 12:07
  • @ThomasWatnedal: I think what sriks says makes sense, lets take it this way, you have a sentence or a string consisting of k words each of length x and separated by k-1 spaces which means the total length of the sentence = k*x + (k-1), now consider the nested loop structure where the outer loop makes a pass over the entire string and inner loop reverses the words. In each reversal you perform x/2 operations per word, hence total reversal cost = k*x/2 and the cost of the algorithm is roughly (k*x)^2 which is a quadratic function. Can you justify your reasoning? – AnkitSablok Feb 14 '15 at 03:02
  • @AnkitSablok: I don't follow how you go from "In each reversal you perform x/2 operations per word, hence total reversal cost = kx/2" to "the cost of the algorithm is roughly (k*x)^2" If you agree that _each word is reversed exactly_ once and that _the algorithmic complexity of reversing each word is linear_, then it is easy to conclude that the entire algorithm is lineaer. – Thomas Watnedal Feb 16 '15 at 10:32
  • Wt about the outer loop then, where are you considering the outer loop cost in your formula? – AnkitSablok Feb 16 '15 at 16:07
  • @AnkitSablok: The outer loop goes though the entire string, but only at every word boundary does it do work that is not O(1) in complexity. That is why the complexity is n + combined length of all words/2 – Thomas Watnedal Feb 19 '15 at 23:06
  • Why use char* array as formal parameter, and not array instead, which would be more consistent? As a beginner I called with a char* as actual parameter but run failed. – Lei Yang Jun 19 '16 at 12:52
4

pushing a string onto a stack and then popping it off - is that still O(1)? essentially, that is the same as using split()...

Doesn't O(1) mean in-place? This task gets easy if we can just append strings and stuff, but that uses space...

EDIT: Thomas Watnedal is right. The following algorithm is O(n) in time and O(1) in space:

  1. reverse string in-place (first iteration over string)
  2. reverse each (reversed) word in-place (another two iterations over string)
    1. find first word boundary
    2. reverse inside this word boundary
    3. repeat for next word until finished

I guess we would need to prove that step 2 is really only O(2n)...

Daren Thomas
  • 67,947
  • 40
  • 154
  • 200
  • O(1) in space means in-place (plus any additional amount of memory if it does not grow with N). – jfs Sep 10 '08 at 16:01
3
#include <string>
#include <boost/next_prior.hpp>

void reverse(std::string& foo) {
    using namespace std;
    std::reverse(foo.begin(), foo.end());
    string::iterator begin = foo.begin();
    while (1) {
        string::iterator space = find(begin, foo.end(), ' ');
        std::reverse(begin, space);
        begin = boost::next(space);
        if (space == foo.end())
            break;
    }
}
SamB
  • 9,039
  • 5
  • 49
  • 56
Leon Timmermans
  • 30,029
  • 2
  • 61
  • 110
  • This *is* the right direction, but using recursion like that breaks the O(1) restriction: The call stack will just keep growing in proportion to the word count in the input. Also, you use reverse(foo.begin(), foo.end()) before the while loop - this is a **BUG**, as there is no way of terminating! – Daren Thomas Sep 07 '08 at 08:58
  • 1
    Oops (concerning above comment): I failed to notice that Leon is using two different reverse() functions, and is thus not using recursion at all. My appologies! Leon, could you please edit your post and add a std:: qualifier to reverse(foo.begin(), fo.end())? – Daren Thomas Sep 07 '08 at 15:09
  • 1
    Added the std:: qualifier to avoid the confusion – Leon Timmermans Sep 10 '08 at 15:00
2

Here is my answer. No library calls and no temp data structures.

#include <stdio.h>

void reverse(char* string, int length){
    int i;
    for (i = 0; i < length/2; i++){
        string[length - 1 - i] ^= string[i] ;
        string[i] ^= string[length - 1 - i];
        string[length - 1 - i] ^= string[i];
    }   
}

int main () {
char string[] = "This is a test string";
char *ptr;
int i = 0;
int word = 0;
ptr = (char *)&string;
printf("%s\n", string);
int length=0;
while (*ptr++){
    ++length;
}
reverse(string, length);
printf("%s\n", string);

for (i=0;i<length;i++){
    if(string[i] == ' '){
       reverse(&string[word], i-word);
       word = i+1;
       }
}   
reverse(&string[word], i-word); //for last word             
printf("\n%s\n", string);
return 0;
}
SamB
  • 9,039
  • 5
  • 49
  • 56
Shivkrish22
  • 121
  • 1
  • 6
  • 1
    `length = sizeof(string)-1;` could be used here. You could extract functions `mystrlen()`, `xor_swap()` for readability. – jfs Sep 14 '10 at 19:09
1

THIS PROGRAM IS TO REVERSE THE SENTENCE USING POINTERS IN "C language" By Vasantha kumar & Sundaramoorthy from KONGU ENGG COLLEGE, Erode.

NOTE: Sentence must end with dot(.) because NULL character is not assigned automatically at the end of the sentence*

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

int main()
{
char *p,*s="this is good.",*t;
int i,j,a,l,count=0;

l=strlen(s);

p=&s[l-1];

t=&s[-1];
while(*t)
   {
      if(*t==' ')
     count++;
     t++;
   }
   a=count;
  while(l!=0)
   {
for(i=0;*p!=' '&&t!=p;p--,i++);
   p++;

  for(;((*p)!='.')&&(*p!=' ');p++)
    printf("%c",*p);
  printf(" ");
  if(a==count)
   {
     p=p-i-1;
     l=l-i;
   }
  else
   {
     p=p-i-2;
     l=l-i-1;
   }

count--;
  }

return 0;  
}
1

In pseudo code:

reverse input string
reverse each word (you will need to find word boundaries)
aku
  • 122,288
  • 32
  • 173
  • 203
1

In C: (C99)

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

void reverseString(char* string, int length)
{
    char swap;
    for (int i = 0; i < length/2; i++)
    {
        swap = string[length - 1 - i];
        string[length - 1 - i] = string[i];
        string[i] = swap;
    }   
}

int main (int argc, const char * argv[]) {
    char teststring[] = "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.";
    printf("%s\n", teststring);
    int length = strlen(teststring);
    reverseString(teststring, length);
    int i = 0;
    while (i < length)
    {
        int wordlength = strspn(teststring + i, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
        reverseString(teststring + i, wordlength);
        i += wordlength + 1;
    }
    printf("%s\n", teststring);
    return 0;
}

This gives output:

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.

.it in )characters not( words the of order the reverse to algorithm efficient an give ,words of sentence a form which characters of array an Given

This takes at most 4N time, with small constant space. Unfortunately, It doesn't handle punctuation or case gracefully.

Community
  • 1
  • 1
smh
  • 1,233
  • 1
  • 12
  • 16
1

O(N) in space and O(N) in time solution in Python:

def reverse_words_nosplit(str_):
  """
  >>> f = reverse_words_nosplit
  >>> f("this is a string")
  'string a is this'
  """
  iend = len(str_)
  s = ""
  while True:
    ispace = str_.rfind(" ", 0, iend)
    if ispace == -1:
      s += str_[:iend]
      break
    s += str_[ispace+1:iend]
    s += " "
    iend = ispace
  return s
SamB
  • 9,039
  • 5
  • 49
  • 56
jfs
  • 399,953
  • 195
  • 994
  • 1,670
1

You would use what is known as an iterative recursive function, which is O(N) in time as it takes N (N being the number of words) iterations to complete and O(1) in space as each iteration holds its own state within the function arguments.

(define (reverse sentence-to-reverse)
  (reverse-iter (sentence-to-reverse ""))

(define (reverse-iter(sentence, reverse-sentence)
  (if (= 0 string-length sentence)
    reverse-sentence
    ( reverse-iter( remove-first-word(sentence), add-first-word(sentence, reverse-sentence)))

Note: I have written this in scheme which I am a complete novice, so apologies for lack of correct string manipulation.

remove-first-word finds the first word boundary of sentence, then takes that section of characters (including space and punctuation) and removes it and returns new sentence

add-first-word finds the first word boundary of sentence, then takes that section of characters (including space and punctuation) and adds it to reverse-sentence and returns new reverse-sentence contents.

SamB
  • 9,039
  • 5
  • 49
  • 56
Xian
  • 76,121
  • 12
  • 43
  • 49
1

@Daren Thomas

Implementation of your algorithm (O(N) in time, O(1) in space) in D (Digital Mars):

#!/usr/bin/dmd -run
/**
 * to compile & run:
 * $ dmd -run reverse_words.d
 * to optimize:
 * $ dmd -O -inline -release reverse_words.d
 */
import std.algorithm: reverse;
import std.stdio: writeln;
import std.string: find;

void reverse_words(char[] str) {
  // reverse whole string
  reverse(str);

  // reverse each word
  for (auto i = 0; (i = find(str, " ")) != -1; str = str[i + 1..length])
    reverse(str[0..i]);

  // reverse last word
  reverse(str);
}

void main() {
  char[] str = cast(char[])("this is a string");
  writeln(str);
  reverse_words(str);
  writeln(str);
}

Output:

this is a string
string a is this
jfs
  • 399,953
  • 195
  • 994
  • 1,670
1

in Ruby

"this is a string".split.reverse.join(" ")

0

in C#, in-place, O(n), and tested:

static char[] ReverseAllWords(char[] in_text)
{
    int lindex = 0;
    int rindex = in_text.Length - 1;
    if (rindex > 1)
    {
        //reverse complete phrase
        in_text = ReverseString(in_text, 0, rindex);

        //reverse each word in resultant reversed phrase
        for (rindex = 0; rindex <= in_text.Length; rindex++)
        {
            if (rindex == in_text.Length || in_text[rindex] == ' ')
            {
                in_text = ReverseString(in_text, lindex, rindex - 1);
                lindex = rindex + 1;
            }
        }
    }
    return in_text;
}

static char[] ReverseString(char[] intext, int lindex, int rindex)
{
    char tempc;
    while (lindex < rindex)
    {
        tempc = intext[lindex];
        intext[lindex++] = intext[rindex];
        intext[rindex--] = tempc;
    }
    return intext;
}
SamB
  • 9,039
  • 5
  • 49
  • 56
Demi
  • 6,147
  • 7
  • 36
  • 38
0

Efficient in terms of my time: took under 2 minutes to write in REBOL:

reverse_words: func [s [string!]] [form reverse parse s none]

Try it out: reverse_words "this is a string" "string a is this"

0

This problem can be solved with O(n) in time and O(1) in space. The sample code looks as mentioned below:

    public static string reverseWords(String s)
    {

        char[] stringChar = s.ToCharArray();
        int length = stringChar.Length, tempIndex = 0;

        Swap(stringChar, 0, length - 1);

        for (int i = 0; i < length; i++)
        {
            if (i == length-1)
            {
                Swap(stringChar, tempIndex, i);
                tempIndex = i + 1;
            }
            else if (stringChar[i] == ' ')
            {
                Swap(stringChar, tempIndex, i-1);
                tempIndex = i + 1;
            }
        }

        return new String(stringChar);
    }

    private static void Swap(char[] p, int startIndex, int endIndex)
    {
        while (startIndex < endIndex)
        {
            p[startIndex] ^= p[endIndex];
            p[endIndex] ^= p[startIndex];
            p[startIndex] ^= p[endIndex];
            startIndex++;
            endIndex--;
        }
    }
0

A one liner:

l="Is this as expected ??"
" ".join(each[::-1] for each in l[::-1].split())

Output:

'?? expected as this Is'
serenesat
  • 4,611
  • 10
  • 37
  • 53
aady
  • 35
  • 2
0

Algorithm: 1).Reverse each word of the string. 2).Reverse resultant String.

public class Solution {
public String reverseWords(String p) {
   String reg=" ";
  if(p==null||p.length()==0||p.equals(""))
{
    return "";
}
String[] a=p.split("\\s+");
StringBuilder res=new StringBuilder();;
for(int i=0;i<a.length;i++)
{

    String temp=doReverseString(a[i]);
    res.append(temp);
    res.append(" ");
}
String resultant=doReverseString(res.toString());
System.out.println(res);
return resultant.toString().replaceAll("^\\s+|\\s+$", ""); 
}


public String doReverseString(String s)`{`


char str[]=s.toCharArray();
int start=0,end=s.length()-1;
while(start<end)
{
char temp=str[start];
str[start]=str[end];
str[end]=temp;
start++;
end--;
}
String a=new String(str);
return a;

}

public static void main(String[] args)
{
Solution r=new Solution();
String main=r.reverseWords("kya hua");
//System.out.println(re);
System.out.println(main);
}
}
zoha khan
  • 21
  • 2
0

A Ruby solution.

# Reverse all words in string
def reverse_words(string)
  return string if string == ''

  reverse(string, 0, string.size - 1)

  bounds = next_word_bounds(string, 0)

  while bounds.all? { |b| b < string.size }
    reverse(string, bounds[:from], bounds[:to])
    bounds = next_word_bounds(string, bounds[:to] + 1)
  end

  string
end

# Reverse a single word between indices "from" and "to" in "string"
def reverse(s, from, to)
    half = (from - to) / 2 + 1

    half.times do |i|
        s[from], s[to] = s[to], s[from]
        from, to = from.next, to.next
    end

    s
end

# Find the boundaries of the next word starting at index "from"
def next_word_bounds(s, from)
  from = s.index(/\S/, from) || s.size
  to = s.index(/\s/, from + 1) || s.size

  return { from: from, to: to - 1 }
end
SamB
  • 9,039
  • 5
  • 49
  • 56
Anurag
  • 140,337
  • 36
  • 221
  • 257
0

The algorithm to solve this problem is based on two steps process, first step will reverse the individual words of string,then in second step, reverse whole string. Implementation of algorithm will take O(n) time and O(1) space complexity.

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

      void reverseStr(char* s, int start, int end);

      int main()
      {
              char s[] = "This is test string";

              int start = 0;
              int end = 0;
              int i = 0;

              while (1) {

              if (s[i] == ' ' || s[i] == '\0')
              {
                    reverseStr(s, start, end-1);
                    start = i + 1;
                    end = start;
              }
              else{
                    end++;
              }

              if(s[i] == '\0'){
                   break;
              }
              i++;
      }

      reverseStr(s, 0, strlen(s)-1);
      printf("\n\noutput= %s\n\n", s);

      return 0;
  }

  void reverseStr(char* s, int start, int end)
  {
     char temp;
     int j = end;
     int i = start;

     for (i = start; i < j ; i++, j--) {
          temp = s[i];
          s[i] = s[j];
          s[j] = temp;
     }
 }
Himanshu Mahajan
  • 4,779
  • 2
  • 36
  • 29
0

Push each word onto a stack. Pop all the words off the stack.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • It does not meet the requirement of O(1) in space (Number of words is O(N)) – jfs Sep 10 '08 at 16:06
  • Holding a string in memory is O(N) in space already. Since 2*O(N) = O(N), I'm not sure what you prove. And at any rate, any algorithm that moves a word at a time instead of a letter at a time will have a space complexity O(M), where M is the largest word. If that's the constraint then the answer is: 1) Reverse the character array (using the well-known tricks for reversing the array). 2) At each word boundary, reverse the characters within the boundary. – Jason Jul 26 '10 at 18:06
  • 1
    @Jason: 1. O(1) in space means in-place (plus any additional amount of memory if it does not grow with N). 2. you can reverse a word inplace too (as the accepted answer demonstrates) – jfs May 17 '15 at 14:34
0

A C++ solution:

#include <string>
#include <iostream>
using namespace std;

string revwords(string in) {
    string rev;
    int wordlen = 0;
    for (int i = in.length(); i >= 0; --i) {
        if (i == 0 || iswspace(in[i-1])) {
            if (wordlen) {
                for (int j = i; wordlen--; )
                    rev.push_back(in[j++]);
                wordlen = 0;
            }
            if (i > 0)
                rev.push_back(in[i-1]);
        }
        else
            ++wordlen;
    }
    return rev;
}

int main() {
    cout << revwords("this is a sentence") << "." << endl;
    cout << revwords("  a sentence   with extra    spaces   ") << "." << endl;
    return 0;
}
SamB
  • 9,039
  • 5
  • 49
  • 56
Ferruccio
  • 98,941
  • 38
  • 226
  • 299
0
using System;

namespace q47407
{
    class MainClass
    {
        public static void Main(string[] args)
        {
            string s = Console.ReadLine();
            string[] r = s.Split(' ');
            for(int i = r.Length-1 ; i >= 0; i--)
                Console.Write(r[i] + " ");
            Console.WriteLine();

        }
    }
}

edit: i guess i should read the whole question... carry on.

John Boker
  • 82,559
  • 17
  • 97
  • 130