3

There's a simple string manipulation problem where you are required to reverse the words of each line: http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=121&page=show_problem&problem=424

So:

I love you.
You love me.
We're a happy family.

Would become:

I evol .uoy
uoY evol .em
er'eW a yppah .ylimaf

Now, I've written a simple java solution which looks something like:

BufferedReader file = new BufferedReader(new InputStreamReader(System.in));
String s;
while((s=file.readLine())!=null){
    String[] sr = s.split(" ");
    for(int i = 0; i<sr.length; i++)
        System.out.print(new StringBuffer(sr[i]).reverse() + (i==sr.length-1?"\n":" "));
}

Because I am trying to learn c++, I've also tried writing a c++ solution which looks like:

string s;
while(getline(cin, s)){
    string tmp = "";
    for(int i = 0; i<=s.length(); i++)
        if( i==s.length() || s[i] == ' '){
            for(int j = tmp.length(); j>=0; j--)
                cout << tmp[j];
            if( i == s.length()) cout << endl;
            else cout << " ";
            tmp = "";
        }else
            tmp += s[i];
}

My questions are:

  1. The c++ solution returns "wrong answer", whereas the java one is accepted, why?
  2. What improvements, if any, can be made to the c++ solution?
Andrew Liu
  • 666
  • 4
  • 7

6 Answers6

3
std::string line_string;
while ( getline( std::cin, line_string ) ) {

    // Instead of `split`, read out of a std::istringstream:
    std::istringstream line_stream( line_string );
    std::string word;
    while ( line_stream >> word ) {

        // Use std::reverse instead of a loop:
        std::reverse( word.begin(), word.end() );

        // Always qualify with std:: instead of using namespace std;
        std::cout << word << ' ';
    }
    std::cout << '\n'; // prefer '\n' to std::endl unless you need a flush
}

http://ideone.com/hd3bg

If this doesn't pass, it's probably because of the trailing space at the end of each line. Use a Boolean variable to avoid printing space before newline.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • There are ways other than a boolean. I particularly like Jerry's answer [here](http://stackoverflow.com/questions/3496982/printing-lists-with-commas-c). It may be longer than what you might need once, but it's designed quite well to use more than once. The pre-made, versatile, reusable stuff comes in handy when you want a quick solution. – chris Jun 22 '12 at 05:14
  • @chris: Wow, that's a bit heavy duty. Actually the standard library has a similar idiom in `std::ostream_iterator`, I'll adjust this answer. – Potatoswatter Jun 22 '12 at 05:18
  • That's true, I forgot about that one. The infix has the advantage of working for other uses like copying data around, but the `ostream_iterator` works well for printing. – chris Jun 22 '12 at 05:24
  • @chris: woops, I was wrong, `ostream_iterator` implements a delimiter but stupidly prints it unconditionally :v( – Potatoswatter Jun 22 '12 at 05:25
  • Does it really? I still would've expected that to pass through the usefulness test. – chris Jun 22 '12 at 05:27
3

If boost is allowed, I would use a combination of boost::split, boost::join and std::reverse.

std::string line;
std::vector<std::string> vs;
while (getline(std::cin, line)) {       
    boost::split(vs, line, ::isspace);
    for (auto & word : vs)
        std::reverse(word.begin(), word.end());
    std::cout << boost::join(vs, " ") << '\n';
}

If boost is not available, I would have those functions (or similar) in my personal library and I would just copy them into my submission.

Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
  • I don't think `boost` library is allowed on the online judge. This is probably good as a general answer, though. – nhahtdh Jun 22 '12 at 05:25
  • The fact that Boost probably won't be on an online judge is the reason this answer *isn't* general. It's nice to know about, though. – Potatoswatter Jun 22 '12 at 05:29
1

the reason of wrong is:

string s;
while(getline(cin, s)){
    string tmp = "";
    for(int i = 0; i<=s.length(); i++)
        if( i==s.length() || s[i] == ' '){
            for(int j = tmp.length(); j>=0; j--)
                        ^^^^^^^^^^^^ => the first tmp[j] is \0!
                                     => should be tmp.length()-1
                cout << tmp[j];
            if( i == s.length()) cout << endl;
            else cout << " ";
            tmp = "";
        }else
            tmp += s[i];
}

remember, in c/c++, an index starts from 0.

lenx.wei
  • 86
  • 3
  • Sorry but did you check the output of tmp[0] ? In C++ a string has length so it doesn't end with '\0' – lollancf37 Jun 22 '12 at 10:13
  • @lollancf37: Actually in C++11 (and in practice it's true in C++03 too) `std::string` _does_ end in '\0', it's just not counted in its length, see 21.4.5/2 and 21.4.7/1. – Jonathan Wakely Jun 22 '12 at 13:17
  • @JonathanWakely thanks. However I still don't think that the issue brought here doesn't apply. – lollancf37 Jun 22 '12 at 13:20
  • @JonathanWakely in c/c++/c++11, all string length don't count the ending \0. However, tmp[length()] points to the \0 or something else if not \0. for c++11 21.4.7.1, c_str()[size()] is valid, so you can guarantee there is a \0 in the end still. – lenx.wei Jun 22 '12 at 20:41
1

If you absolutly want to create a new variable to handle the reverse operation instead of simple printing in the reverse order. Then you have to take in consideration the fallowing : In C++ the index is not the length of a string. Because the index starts at 0, so you start from the index 0 to the index : length - 1

If you want to start C++, after you're familiar with the basics it's always good to digg into the STL before going anywhere else (that's just my opinion).

Here is some code :

string s;  
while(getline(cin, s))
{      
   string tmp = "";      
   for(int i = s.length() - 1; i >= 0; i++)
       std::cout<<s[i];
}
lollancf37
  • 1,105
  • 3
  • 15
  • 27
0

I think if I was going to do this, I'd reverse each word by constructing a new string, passing word.rbegin(), word.rend() as the arguments to the constructor.

Using the Line class from one of my previous answers, and the infix_ostream_iterator from another, it could look something like this:

#include "infix_iterator.h"
#include "line"


int main() {
    auto rev_words = [](std::string const &in) -> std::string {
        std::istringstream b(in);
        std::ostringstream r;

        std::transform(std::istream_iterator<std::string>(b),
            std::istream_iterator<std::string>(),
            infix_ostream_iterator<std::string>(r, " "),
            [](std::string const &in){return std::string(in.rbegin(), 
                                                         in.rend());});
        return r.str();
    };

    std::transform(std::istream_iterator<line>(std::cin), 
        std::istream_iterator<line>(), 
        std::ostream_iterator<std::string>(std::cout, "\n"),
        rev_words);
    return 0;
}

Yes, this is probably going a bit overboard on fully-functional style. It's undoubtedly easier to do something like this instead:

std::string line;
while (std::getline(std::cin, line)) {
    std::istringstream buffer(line);
    std::transform(std::istream_iterator<std::string>(buffer),
                   std::istream_iterator<std::string>(),
                   infix_ostream_iterator<std::string>(std::cout , " "), 
                   [](std::string const &in){return std::string(in.rbegin(), 
                                                                in.rend()););
    std::cout << "\n";
}
Community
  • 1
  • 1
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
0

Here the one i tried.

#include <iostream>
#include <string>

 using namespace std;


 int main(){

    string s = "Hello How are you Mr vins what are you doing now";
    string temp, reverse;

   int i,j;
     // Traverse through First string
    for (i=0;i <=s.length(); i++)
   {
if(s[i] == ' ' || s[i] == '\0') {
    //After getting space reverse the previous word and add to reverse string
    for (j = temp.length(); j >=0; j--)
        reverse = reverse + temp[j];

    reverse = reverse + " ";
    temp="";
    continue;
}
temp = temp + s[i];

}

 cout << "\nlets see reversed sentence \n";

   cout << reverse << "\n";
      return 0;
   }
Vins
  • 4,089
  • 2
  • 35
  • 50