So I'm supposed to write a function that takes a vector of strings and sort it alphabetically but based on the last character of each string. For example, {“apple”, “banana”, “carrot”}, would be {“banana“, “apple“, “carrot”} because the last “a” in banana comes before the “e” in “apple” and the “t” in “carrot”.
This is my code so far:
#include <iostream>
#include <vector>
#include <string>
void SortByReverse(std::vector<std::string> &v)
{
std::vector<int> int_v;
for(int i = 0; i < v.size(); i++) // for loop to store ascii values of each last ch
{
int last_ch_index = v[i].size() - 1;
int_v.push_back(int(v[i][last_ch_index]));
std::sort(int_v.begin(), int_v.end()); // sort ascii value vector in ascending order
}
for(int u = 0; u < int_v.size(); u++) // iterate through ascii values vector and print corresponding str
{
for(int q = 0; q < v.size(); q++)
{
int last_ch_index = v[q].size() - 1;
if(int_v[u] == int(v[q][last_ch_index]))
{
std::cout << v[q] << "\n";
}
}
}
}
int main()
{
std::vector<std::string> v = {"apple", "banana", "carrot"};
SortByReverse(v);
}
which outputs my strings in the correct order but I don't know how to change the order in the vector itself. I tried doing
v[u] = v[q]
but that outputs "banana, banana, carrot" because it assigns "banana" to "apple" to when "apple" is supposed to be printed, you just get "banana" again. Surely there is a way to add on to the code I already have and fix this issue but I don't know how.
If sorting the original vector by back letter is your goal, there is much neater method:
std::sort(v.begin(),v.end(),[](string& lhs, string& rhs)(return <expr>;)); will sort the std::string vector by the <expr> criteria. make <expr> expression return true or false based on the back letter.
std::string has member function back() which has access to the back element, which is last char.
As #paddy pointed out, if you wish to make stable program, make <expr> able to handle empty string & error check.
As your program seems to be a homework, you figure out where to put it.
Related
I have a std::vector<string> where each element is a word. I want to print the vector without repeated words!
I searched a lot on the web and I found lots of material, but I can't and I don't want to use hash maps, iterators and "advanced" (to me) stuff. I can only use plain string comparison == as I am still a beginner.
So, let my_vec a std::vector<std::string> initialized from std input. My idea was to read all the vector and erase any repeated word once I found it:
for(int i=0;i<my_vec.size();++i){
for (int j=i+1;j<my_vec.size();++j){
if(my_vec[i]==my_vec[j]){
my_vec.erase(my_vec.begin()+j); //remove the component from the vector
}
}
}
I tried to test for std::vector<std::string> my_vec{"hey","how","are","you","fine","and","you","fine"}
and indeed I found
hey how are you fine and
so it seems to be right, but for instance if I write the simple vector std::vector<std::string> my_vec{"hello","hello","hello","hello","hello"}
I obtain
hello hello
The problem is that at every call to erase the dimension gets smaller and so I lose information. How can I do that?
Minimalist approach to your existing code. The auto-increment of j is what is ultimately breaking your algorithm. Don't do that. Instead, only increment it when you do NOT remove an element.
I.e.
for (int i = 0; i < my_vec.size(); ++i) {
for (int j = i + 1; j < my_vec.size(); ) { // NOTE: no ++j
if (my_vec[i] == my_vec[j]) {
my_vec.erase(my_vec.begin() + j);
}
else ++j; // NOTE: moved to else-clause
}
}
That is literally it.
You can store the element element index to erase and then eliminate it at the end.
Or repeat the cycle until no erase are performed.
First code Example:
std::vector<int> index_to_erase();
for(int i=0;i<my_vec.size();++i){
for (int j=i+1;j<my_vec.size();++j){
if(my_vec[i]==my_vec[j]){
index_to_erase.push_back(j);
}
}
}
//starting the cycle from the last element to the vector of index, in this
//way the vector of element remains equal for the first n elements
for (int i = index_to_erase.size()-1; i >= 0; i--){
my_vec.erase(my_vec.begin()+index_to_erase[i]); //remove the component from the vector
}
Second code Example:
bool Erase = true;
while(Erase){
Erase = false;
for(int i=0;i<my_vec.size();++i){
for (int j=i+1;j<my_vec.size();++j){
if(my_vec[i]==my_vec[j]){
my_vec.erase(my_vec.begin()+j); //remove the component from the vector
Erase = true;
}
}
}
}
Why don't you use std::unique?
You can use it as easy as:
std::vector<std::string> v{ "hello", "hello", "hello", "hello", "hello" };
std::sort(v.begin(), v.end());
v.erase(std::unique(v.begin(), v.end()), v.end());
N.B. Elements need to be sorted because std::unique works only for consecutive duplicates.
In case you don't want to change the content of the std::vector, but only have stable output, I recommend other answers.
Erasing elements from a container inside a loop is a little tricky, because after erasing element at index i the next element (in the next iteration) is not at index i+1 but at index i.
Read about the erase-remove-idiom for the idomatic way to erase elements. However, if you just want to print on the screen there is a much simpler way to fix your code:
for(int i=0; i<my_vec.size(); ++i){
bool unique = true;
for (int j=0; j<i; ++j){
if(my_vec[i]==my_vec[j]) {
unique = false;
break;
}
if (unique) std::cout << my_vec[i];
}
}
Instead of checking for elements after the current one you should compare to elements before. Otherwise "bar x bar y bar" will result in "x x bar" when I suppose it should be "bar x y".
Last but not least, consider that using the traditional loops with indices is the complicated way, while using iterators or a range-based loop is much simpler. Don't be afraid of new stuff, on the long run it will be easier to use.
You can simply use the combination of sort and unique as follows.
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<std::string> vec{"hey","how","are","you","fine","and","you","fine"};
sort(vec.begin(), vec.end());
vec.erase(unique(vec.begin(), vec.end() ), vec.end());
for (int i = 0; i < vec.size(); i ++) {
std::cout << vec[i] << " ";
}
std::cout << "\n";
return 0;
}
I wrote this program to find if a string is contained in another string (see paragraph below this, I tried to explain what I want it to do). When I test it, sometimes it works, most of the times it gives me the error "String subscript out of range". I'm very new to C++, I'd appreciate someone to tell me how can I improve this code or why I'm being dumb, because I really don't get why it doesn't work.
what i want this to do is find if string one can be found in string way;
so i want it to check for every letter of string way if the letter [i] is equal to the first letter of the string one (way[i+0]==one[0]),
and way[i+1]==one[1] and so on for all letters in one.
so for example way = abankjve and one = ank
it takes the first letter in way (a) and gets the first letter in one(a). the're equal. but we see that way[0+1] is not equal to one[1]. so o can't be true.
it goes on like this till it gets to way[2]=a. way[2+0]=one[0]. o is true. then it checks way[2+1]=one[1]. true! then it checks way[2+2]=one[2]. true! then
one is contained in way.
#include <iostream>
using namespace std;
int main()
{
string way, one;
bool o=false;
cin >> way;
cin >> one;
for (int i = 0; i < way.size(); i++)
{
for (int k = 0; k < one.size(); k++)
{
if (way[i + k]==one[k])
{
o = true;
}
}
}
cout << o << endl;
}
If you think about it, way[i+k] will result in index out of range.
say way is length of 5 and one is length of 3.
i+k's range is 0 <= i + k <= 6. Witch is bigger than the possible index of way.
change first for loop for (int i = 0; i < way.size(); i++) to
for (int i = 0; i <= (int)way.size() - one.size(); i++)
Note I've used static_cast to int. *.size() returns unsigned int so if one's size is bigger than way's size, the result won't be what you've imagined.
#include <iostream>
#include <string>
int main()
{
std::string way, one;
std::cin >> way;
std::cin >> one;
bool found{};
for (size_t i = 0; i < way.size() - one.size()+1; i++)
{
if(one == way.substr(i, one.size())) {
found = true;
break;
}
}
std::cout << found;
}
Demo
I'm trying to loop through a word but only vowels are working for some reason. The other type prints out the amount of words in total. Please excuse my poor choise of words mixed with Swedish and English.
#include <iostream>
using namespace std;
int main()
{
int vo = 0;
int ko = 0;
char vocals[7]
{
'A','O','U','E','I','Y'
};
char konsonanter[19]
{
'B','C','D','F','G','H','J','K','L','M','N','P','Q','R','S','T','V','X','Z'
};
string word = "Toysoldier";
for(int i = 0; i < word.length(); i++)
{
for(int v = 0; v < vocals[v]; v++)
{
if(vocals[v] == word[i])
vo++;
}
for(int k = 0; k < konsonanter[k]; k++)
{
if(konsonanter[k] == word[i])
ko++;
}
}
cout << "Konsonanter = " << ko << " Vokaler = " << vo << endl;
}
You got your loop condition wrong.
Instead of v < vocals[v] you probably meant to write v < 7 as there are 7 vocals in your array.
Writing v < vocals[v] is not an error as v is initialized to zero while the expression vocals[v] is a char which can also be used in array subscripts ('A' is translated to 65, 'E' to 69 and so on).
The same problem exists in the loop for consonants.
You have more than one bug, so I'll just remark on all the code in order.
If you write
char vocals[7] = {'A' ... 'Y'};
you have to manually keep the array size and the initializer in sync - here, for example, you missed one (you have 6 "vocals" in your initializer for a 7-element array).
It's less error-prone to write
char vowels[] = {'A', 'E', 'I', 'O', 'U'};
and let the compiler count it for you. (I renamed it to "vowels" because that's what they're called in English, and removed 'Y' because it's not one - obviously you can stick with whatever works in Swedish.)
Similarly, this loop is wrong:
for(int v = 0; v < vocals[v]; v++)
because you're comparing v (an index into your array) with vocals[v] which is a character value. They're two different types of thing, and comparing doesn't make sense here. The loop over an array would normally be written
for (int v = 0; v < sizeof(vocals)/sizeof(vocals[0]); ++v)
so for some N-element array, the index v ranges from 0 to N-1. A simpler way is to just write
for (auto v : vocals)
where v is now a character, taking the value of each vocal/vowel one-by-one in the loop body. The compiler can figure out the array size for you.
I'd honestly re-write all your loops in this style: you don't need to manage array indices and bounds manually unless you're doing something more complicated.
On top of the loop condition bugs, your character comparisons will anyway mostly fail: the vowel/vocal and consonant arrays both contain only upper-case letters, and your string is mostly lower-case. You need to manage this, for example with
#include <cctype>
// ...
for (auto c : word) {
for (auto v : vowels) {
if (v == std::toupper(c)) {
Your error is in the tests used in the "for" loops:
v < vocals[v] will compare the integer v to the integer value of the character vocals[v], which happens to be the ASCII code of that character (in practice, that'll be something between 65 and 90, look up "Ascii table" on the web).
To fix your code, you should modify your "for" loops like this:
for (char vocal : vocals) {
if (word[i] == vocal) vo++;
}
for (char konsonant : konsonanter) {
if (word[i] == vocal) ko++;
}
Also, note that you got the number of vocals wrong in your "vocals" array: it's 6, not 7.
You conditions in the loops are flawed, you should use 'for (int v = 0; v < sizeof(vocals); v++)`.
Also, check your condition for a vocal and consonant: What happens if you consider lowercase chars? 'a' != 'A', so you don't count them correctly.
Hint: Use toupper() or tolower() so you don't need to have excessive lists of chars.
I am attending a coding competitions in February and I'm looking at last years problem set. The task I am trying to do is this. As input i am given an integer N(maximum 10,000,000), and a string S of length N. The string consists of only the letters 'x', 'y' and 'z'.
These letter are all 'commands'. You start out with a word consisting of only the letter 'A'. If you get a command 'x' you will add an 'A' to the back of you word.
Command'y' will add a 'B' at the front of the word. And 'y' will flip the word. So an input of N = 3, and S = "xyz", will make the word be "AAB". x: add 'A' ,y: add 'B' and z: flip the entire word.
This entire thing has to be done in under 2 seconds, which seems to be a problem for me to achieve.
I hope all that was understandable...
Well, the solution explanation said that a double-ended queue would be the most efficient way to do this, but it I can't get it any lower than a little more than 10 seconds execution time. Could someone please help me find a way to optimize this code.
using namespace std;
int main() {
int num = 10000000;
string commands = "";
bool reversed = false;
deque<char>word = { 'A' };
// just for generating the input. The real program would not need this
for (int i = 0; i < num / 5; i++) {
commands += "xyzxy'";
}
//cin >> num >> commands;
for (int i = 0; i < num; i++) {
if (commands.at(i) == 'x') { //If the command is 'x' add an A at the back
if (!reversed)
word.push_back('A');
else // if it the word is reversed, reverse the command
word.push_front('A');
}
else if (commands.at(i) == 'y') { //If the command is 'y', add a 'B' at the front
if (!reversed)
word.push_front('B');
else // if it the word is reversed, reverse the command
word.push_back('B');
}
else if (commands.at(i) == 'z') { // If the command is 'z' set the status to reversed/!reversed
reversed = !reversed;
}
}
if (reversed)
reverse(word.begin(), word.end());
for (int i = 0; i < word.size(); i++) { // print out the answer
cout << word.at(i);
}
system("pause");
return 0;
}
Thank you!
Usually we expect that a server executes 10^6 commands per second. Bearing in mind that N=10,000,000 and time limit is 2 seconds, O(n) complexity is required.
You can achieve that using the following technique:
Use a double linked list.
Use a boolean variable, for example flag, that gives true if result string starts at the front of linked list and false if the string starts at the back of the linked list.
For each character of input string push_back if given character is x, push_front if given character is y and change the value of flag.
When reading input finishes, print the string respectively to flag value.
Complexity: O(n)
The trick has two parts.
Use std::deque, which is optimized for insertion at both ends of the container.
Do not flip the word for the 'z' input. Instead, keep track of how many times 'z' was read, and change the code that adds A and B so that if the number of times is odd, the character is added to the other end of the word.
At the conclusion of the input, flip the string just once, if the final count is odd.
The main trick is to avoid wasting time flipping the string over and over again. You only need to flip it at most once.
Observation: push_back on vector is faster than both push_back and push_front on deque.
If you use two vectors (front and back) and treat the front as if it is reversed, then you can also eliminate all of the 'if (reverse)...' stuff by using push_back on the back vector for the x, push_back on the front vector for the y, and swapping the vectors when you hit a z.
Then when outputting the result, do a reverse iteration through the front vector and a forward iteration through the back vector.
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <string>
#include <chrono>
int main(int argc, char* argv[])
{
auto fileIn = std::ifstream{"flip.in"};
int commandSize;
std::string commands;
fileIn >> commandSize >> commands;
std::vector<char> wordFront;
std::vector<char> wordBack;
wordFront.push_back('A');
auto start = std::chrono::high_resolution_clock::now();
for (auto v : commands)
{
switch(v)
{
case 'x':
wordBack.push_back('A');
break;
case 'y':
wordFront.push_back('B');
break;
case 'z':
std::swap(wordFront, wordBack);
break;
}
}
auto finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = finish - start;
std::cout << "Elapsed time (secs): " << elapsed.count() << std::endl;
auto fileOut = std::ofstream{"flip.out"};
std::for_each(crbegin(wordFront), crend(wordFront), [&fileOut](auto v){ fileOut << v; });
std::for_each(cbegin(wordBack), cend(wordBack), [&fileOut](auto v){ fileOut << v; });
}
It didn't make a positive difference on my machine, but you could also reserve some space on the front and back vectors before you start building the word.
Sorry for the title, but I really have no idea what the problem is. The code looks like that (here it has no sense, but in the bigger project is has, so please, do not ask "why do you want to do....")
#include <iostream>
#include <vector>
#include <fstream>
using namespace std;
string sort (string slowo){
string litery = slowo;
for (int i=0; i<litery.length()-1; i++)
for (int j=0; j<litery.length()-1; j++)
if (litery[j]>litery[j+1])
swap(litery[j], litery[j+1]); // (3)
return litery;
}
int main()
{
fstream wordlist;
wordlist.open("wordlist_test",ios::in);
vector<string> words;
while (!wordlist.eof()){ // (4)
bool ok = true;
string word;
getline(wordlist,word);
string sorted = sort(word);
if (ok){
cout<<word<<endl; // (1)
words.push_back(word);
}
}
for (int i = 0; i<words.size(); i++){
cout<<words[i]<<endl; // (2)
}
}
There are for words in file "wordlist_tests". Program at the end should just write them to vector and write what's in vector into standard output. The problem is:
however line(1) proves that all words are ok
vector appears to be
empty in line (2)
now iteresting (probably just for me) part:
there are two ways to make it right:
I can just remove line(3) (however, if I am right, as the variable is passed to sort function through the value, it just swap two letters in independent variable; it has nothing to do with my vector), or:
I can change condition in while loop (4).
for example just like this:
int tmp = 0;
while (tmp < 5){
tmp++;
/..../
What is wrong with this code? What should I do write these words down to vector but still sort them and using this while loop? I cannot find the connection between this things (ok, I see that connection is variable word, but I do not know in what way). Any help appreciate.
What happens in swap() if one of the words is the empty sting ""?
If this happens, litery = "".
The condition in the loops will be to iterate from 0 to (unsigned) 0 - 1, which is a very large number.
You'll then execute if (litery[0] > litery[1])
litery[1] will access beyond the end of the empty string, which causes undefined behavior.
Let's fix this:
The common fix for this, is to iterate from 1 to string.length(). Here's an example:
string sort (string litery){
for (int i=1; i<litery.length(); i++)
for (int j=1; j<litery.length(); j++)
if (litery[j-1]>litery[j])
swap(litery[j-1], litery[j]);
return litery;
}