0

Today,I was making one program,basically it takes routes of cities you have visited in past,and at the end it should return a whole route of your trip,routes.

In first line you're entering number number of cities you visited in past(including your home city), N.

Let's assume that our home city is Paris. In second line you enter your home town (Paris in our case).

In next N lines you enter your trip routes, they don't need to be in order as you did them, so they can be in any order:

Input:

4  
Paris  
Berlin-Paris  
Paris-Zagreb  
Ljubljana-Berlin   
Zagreb-Ljubljana  

Output:

Paris-Zagreb-Ljubljana-Berlin-Paris

This is my code written in c++ :

#include <iostream>
#include <string>
using namespace std;
string s[1000],city;
int n;  

int main(){
    cin>>n;
    cin>>city;
    for(int i=0;i<n;i++){
        cin>>s[i];
    }
    cout<<city;
    for(int j=0;j<n;j++){
        for(int i=0;i<n;i++){
            if(s[i].substr(0,city.length())==city){
                city=s[i].substr(city.length()+1);
            cout<<"-"<<city;
            s[i]='\0';
            }   
        }   
    }


}

It's not the most efficient way to do it,I know...And it only work with single word cities(doesn't work for New York and other 2+ worded cities)

Now my question is,is there a simple way to do it with 2+ worded cities,like New York?
I tried using getline(cin,city) and getline(cin,s[i]),but it's not working for some reason,maybe I didn't do it correctly.

While trying with getline() I noticed that when I tried to input string s[i] with getline() my loop for(int i=0;i<n;i++) only went to n-1,I mean I couldn't enter n strings in that array For example if I used N=5,I could only enter 4 strings

for(int i=0;i<5;i++){
    getline(cin,s[i]);
}

Let's me input 4 strings,while

for(int i=0;i<5;i++){
    cin>>s[i];
}

Let's me input 5 strings


Also I noticed that if I have some simple code:

#include <iostream>
#include <string>
using namespace std;
string city;
int n;  

int main(){
    cin>>n;
    getline(cin,city);
    cout<<city;
}

I thought that it should output name of the city,but that isn't a case, can somebody explain why?

Maybe I was missing something,that's it...
Thanks for reading,if you know the answer please help me :)

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85

2 Answers2

0

Now my question is,is there a simple way to do it with 2+ worded cities,like New York?

Yes, this should have the same effect, but allows 2+ word cities:

#include <iostream>
#include <string>
using namespace std;
string s[1000],city;
int n;  

int main(){
    string nStr;
    getline(cin, nStr);
    // converting string input to an integer
    n = stoi(nStr);
    getline(cin, city);
    for(int i=0;i<n;i++){
        getline(cin, s[i]);
    }
    cout<<city;
    for(int j=0;j<n;j++){
        for(int i=0;i<n;i++){
            if(s[i].substr(0,city.length())==city){
                city=s[i].substr(city.length()+1);
            cout<<"-"<<city;
            s[i]='\0';
            }   
        }   
    }
}

Input:
5
Paris
Berlin-New York
Paris-Zagreb
Ljubljana-Berlin
Zagreb-Ljubljana
New York-Paris

Output:
Paris-Zagreb-Ljubljana-Berlin-New York-Paris

I thought that it should output name of the city,but that isn't a case, can somebody explain why?

This answer explains cin is placing a newline character in the stream which getline immediately reads in to city and prints a newline and so looks like nothing is happening.

jackw11111
  • 1,457
  • 1
  • 17
  • 34
0

is there a simple way to do it with 2+ worded cities, like New York?

Answer: Yes.

As noted in the answer by @jackw11111, your problem in reading names containing whitespace was due to std::cin that reads only from the beginning of the line up to the first whitespace. So for example attempting to read "New York" with std::cin >> city; would result in city containing "New" while "York" remains in stdin unread.

The solution is to use getline, which will read up until the delimiter character specified (default: '\n').

Now, for the remainder of your problem, reading trip legs with two (or more) cities separated by '-', there are a number of ways you can approach the problem. Since each leg of a trip will include a from and to city, using std::pair provided as part of the STL utility header would provide a convenient way to store related from and to cities. You can then use a std::vector<std::pair<..,..>> to create a vector-of-pairs.

Unless you absolutely are required to read the first integer value for the number of trips taken -- you can do away with it as it isn't really needed to handle the input. In reality all you need is your home city and then you can read as many lines of input as you like and store as many trip from and to city pairs until EOF if reading from a file, or a blank line is entered to indicate end-of-input.

To separate lines read with getline, using a std::stringstream. To read from the std::stringstream, you can use getline once again (with a delimiter of '-') to separate the from-to cities in the line. Placing the line in a stringstream and then reading from the stringstream prevents getline from skipping over the '\n' when the delimiter has been changed to something else.

So in your case you can declare the vector of pairs of strings along with a few additional strings to hold home and help with parsing and you can simply read the home city first, e.g.

#include <iostream>
#include <sstream>
#include <string>
#include <utility>
#include <vector>

int main (void) {

    std::string home, last, trip, total;    /* strings */
    std::vector<std::pair<std::string, std::string>> vp {}; /* vector of pairs */

    std::cout << "enter home city: ";       /* prompt for home city */
    if (!getline (std::cin, home)) {        /* read/validate home city */
        std::cerr << "error: failed to read home-city.\n";
        return 1;
    }

Now just loop reading lines of input and checking if the line is empty to indicate end-of-input. Create a stringstream from each line and then read from the stringstream with getline (this eliminates the need of using substr to parse cities from the line). Add each pair of from-to cities to your vector using std::make_pair:

    /* prompt for trips (any number of legs per-line), ENTER alone when done */
    std::cout << "\nenter trips (format city1-city2[-cityN]) "
                    "[Enter] alone when done\n\ntrip: ";
    while (getline (std::cin, trip)) {      /* while trip entered */
        if (!trip.length())                 /* if empty (zero length) break */
            break;
        std::stringstream ss (trip);        /* create stringsteam from line */
        std::string from, to;               /* strings for from and to cities */
        if (getline (ss, from, '-')) {      /* get from city from stringstrem */
            while (getline (ss, to, '-')) { /* get to city from stringstream */
                vp.push_back(std::make_pair (from, to));  /* add pair to vector */
                from = to;                  /* update from = to */
            }
        }
        std::cout << "trip: ";              /* prompt for next trip */
    }

Finally, just loop over your stored pairs (with nested loops) to pick out all of the trip legs based on a from-to and then find the next by setting from = to; and finding the next match. (you can also add additional checks to make sure that a proper from-to is found on each iteration -- that is left to you -- something simple like bool found; will do)

    total = home;                           /* initialize total & last to home */
    last = home;
    for (auto t1 : vp) {                    /* loop over each trip */
        for (auto t2 : vp) {                /* 2nd search loop */
            if (t2.first == last) {         /* find trip begins with last city */
                total += "-" + t2.second;   /* add trip end to total */
                last = t2.second;           /* update last to end */
                break;                      /* go find next trip */
            }
        }
    }
    std::cout << '\n' << total << '\n';     /* output total trip beg to end */

(note: the first object in each std::pair is accessed using .first and the second using .second -- even somewhat logical...)

There are many variation your can come up with. Putting it altogether, you could do:

#include <iostream>
#include <sstream>
#include <string>
#include <utility>
#include <vector>

int main (void) {

    std::string home, last, trip, total;    /* strings */
    std::vector<std::pair<std::string, std::string>> vp {}; /* vector of pairs */

    std::cout << "enter home city: ";       /* prompt for home city */
    if (!getline (std::cin, home)) {        /* read/validate home city */
        std::cerr << "error: failed to read home-city.\n";
        return 1;
    }

    /* prompt for trips (any number of legs per-line), ENTER alone when done */
    std::cout << "\nenter trips (format city1-city2[-cityN]) "
                    "[Enter] alone when done\n\ntrip: ";
    while (getline (std::cin, trip)) {      /* while trip entered */
        if (!trip.length())                 /* if empty (zero length) break */
            break;
        std::stringstream ss (trip);        /* create stringsteam from line */
        std::string from, to;               /* strings for from and to cities */
        if (getline (ss, from, '-')) {      /* get from city from stringstrem */
            while (getline (ss, to, '-')) { /* get to city from stringstream */
                vp.push_back(std::make_pair (from, to));  /* add pair to vector */
                from = to;                  /* update from = to */
            }
        }
        std::cout << "trip: ";              /* prompt for next trip */
    }

    total = home;                           /* initialize total & last to home */
    last = home;
    for (auto t1 : vp) {                    /* loop over each trip */
        for (auto t2 : vp) {                /* 2nd search loop */
            if (t2.first == last) {         /* find trip begins with last city */
                total += "-" + t2.second;   /* add trip end to total */
                last = t2.second;           /* update last to end */
                break;                      /* go find next trip */
            }
        }
    }
    std::cout << '\n' << total << '\n';     /* output total trip beg to end */
}

Example Use/Output

$ ./bin/triptotal
enter home city: Paris

enter trips (format city1-city2[-cityN]) [Enter] alone when done

trip: Berlin-Paris
trip: Paris-New York
trip: New York-Zagreb
trip: Ljubljana-Berlin
trip: Zagreb-Ljubljana
trip:

Paris-New York-Zagreb-Ljubljana-Berlin-Paris

That could also be entered as:

$ ./bin/triptotal
enter home city: Paris

enter trips (format city1-city2[-cityN]) [Enter] alone when done

trip: San Antonio-New York-Berlin-Paris
trip: Paris-Frankfort-Zagreb
trip: Zagreb-Ljubljana-Dallas
trip: Dallas-San Antonio
trip:

Paris-Frankfort-Zagreb-Ljubljana-Dallas-San Antonio-New York-Berlin-Paris

The one additional task you will have is finding a way to handle multiple trip legs that may visit the same city. (that too is left for you) Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85