The problem I have, I have to add to a vector, the missing chars.
For example I have initially
s,a,p,i,e,n,t,i,a
and I have to add missing chars to it
s,a,p,i,e,n,t,i,a,b,c,d ...
I am trying to use this code to search for an existing value.
for(char c='a';c!='z';++c)
{
if (vec.end()!=find(vec.begin(),vec.end(),c))
vec.push_back(c);
}
The find returns last when it fails to locate a value. But how do I know if last value was in it?
EDIT
When the for loop starts, for 'a' returns vec.end() so it should not go in, but goes in, and adds 'a' again in the end.
See this in debugger
alt text http://img203.imageshack.us/img203/2048/bb1f.jpg
(The bug I have, the value in last position gets inserted twice, I have to omit this)
In your case it's best to:
Create one vector(bool), with indexes from 'a' to 'z' ,initialize it to false, (i)
Run once through your original vector, set true in the other vector the element for each character that you find,
Run once through this new vector and for each false value, append the corresponding char to the original.
(i) You may use actual_index = character - 'a'. Put some assertions here and there so that you don't crash for characters outside the range you are checking, presumably 'a' to 'z' (which by the way is not a strict definition of what a char is).
With just one initialization, two steps linear steps and no searches, you'll be done.
What others have answered is true but you should also change the termination condition in your for loop to c <= 'z' if you want the letter z to be included in your list.
EDIT
I can't help adding that with the Boost.RangeEx library your problem can be solved with a one-liner:
boost::set_difference(boost::counting_range('a', char('z' + 1)),
std::set<char>(vec.begin(), vec.end()),
std::back_inserter(vec));
Nope, end() is not the last element of the vector but past it. To iterate over all elements you normally do
for(it= vec.begin(); it!= vec.end(); it++) ...
So whatever your problem is, this is ok.
When find succeeds it returns iterator which is pointing to the found position. So if the value is in the vector then the return value will be something other that vec.end(). The condition inside the if condition should be == and not != if you are trying to create vector of unique characters.
If you need to find a value in your container, then the greatest likelihood is that you need to use a different sort of container, where searching is fast!
Have a Look at this very useful diagram by Adrinael:
(source: adrinael.net)
(In your case I believe std::set is probably the most appropriate)
vec.end() returns an iterator whose position is past the last element in the vector. If it matches on the last value, the returned iterator will not be equal to vec.end().
You want to insert the character into array if it is NOT found, when it == end.
You are checking if it != end so you insert characters when they are already found in the string. See this:
char v[] = "acef";
vector<char> str(v,v+sizeof(v));
copy(str.begin(), str.end(), ostream_iterator<char>(cout, ","));
cout << endl;
for (char c = 'a'; c < 'g'; ++c)
{
vector<char>::iterator it = find(str.begin(),str.end(), c);
if (it == str.end())
{
str.push_back(c);
}
}
copy(str.begin(), str.end(), ostream_iterator<char>(cout, ","));
output:
a,c,e,f,,
a,c,e,f,,b,d,
The extra empty character ,, is the null in the original string "acef" - null terminated.
you can start by sorting the vector
in this way you will notice instantly for gaps in the sequence
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int main(int argc, char** argv)
{
vector<char> original;
original.push_back('a');
original.push_back('d');
original.push_back('x');
original.push_back('j');
original.push_back('z');
sort(original.begin(), original.end());
vector<char> unseen_chars;
char current_char = 0;
char last_char = original[0];
for (int i = 1; i <= original.size(); i++)
{
current_char = original[i];
for ( char j = last_char + 1; j < current_char; j++)
{
unseen_chars.push_back(j);
}
last_char = current_char;
}
for (int i = 0; i < unseen_chars.size(); i++)
{
cout << unseen_chars[i];
}
cout << endl;
Related
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.
I am reversing a string and comparing it to another string whether it matches or not.
String d is reversed correctly, but in the If statement, it is not comparing. Strings comparing like this usually works I have done it before. Can someone explain this?
Thank You
int main()
{
string a, d, e;
int b, c;
cin >> a >> e;
for (int i = a.size(); i >= 0; i--)
{
d.push_back(a[i]);
}
cout << d;
if (d == e)
cout << "YES";
else
cout << "NO";
return 0;
}
If a string is 4 characters long, then s[4] will give you 1 past the end of the string. So here:
for (int i = a.size(); i >= 0; i--)
when you use i which would be 4 in the first step, you are accessing past the end of the string. You could do:
for (int i = a.size() - 1; i >= 0; i--)
but be careful if a is empty! You will underflow the integer and end up with big problems! You could check for empty string at the top of your function but that is extra work. To avoid this problem, you could use an iterator, but these only go forward :( Oh wait, reverse_iterator!
for (auto rit = s.rbegin(); rit != s.rend(); ++rit) {
d.push_back(*rit);
This will solve the problem. You could wrap this up in a function, call it reverse! Luckily, this actually exists: std::reverse:
a = d;
std::reverse(std::begin(d), std::end(d));
Easy peasy
Note that the i = a.size() when used in d.push_back(a[i]); will lead to undesired subscript access (recall that indexing starts with 0, while size() returns the amount of elements in a container).
You should look toward this implementation:
#include <string>
#include <iostream>
int main()
{
std::string a, d, e;
int b, c;
std::cin >> a >> e;
for (auto i = a.rbegin(); i != a.rend(); ++i) // Using return iterators
{
d.push_back(*i); // Dereference the iterator and push_back() it
}
std::cout << d << std::endl;
if (d == e)
std::cout << "YES" << std::endl;
else
std::cout << "NO" << std::endl;
return 0;
}
Example:
foo
oof
oof
YES
This approach uses reverse iterators on the string. Note, in order to advance this type of iterator, you use the same increment operation, only in this case, the iterator goes backwards (from the end of container to beginning).
The issue is that you accessing the NUL terminator of a, and putting that at the start of d. The behaviour of your program was undefined up to but not including C++11.
Using
a = d;
std::reverse(std::begin(d), std::end(d));
is the solution. Alternatives such as character by character access are poor due to the potential pitfalls with using unsigned arithmetic. Using the C++ standard library can also help your compiler in its optimisation choices.
Rather than reversing the string then comparing, you could compare the strings using std::equal:
bool result = std::equal(a.rbegin(), a.rend(),
d.begin(), d.end());
If all you want is to check if the string is a palindrome, you don't need to allocate a new string. Here's a much more efficient way:
#include <algorithm>
#include <string_view>
[[nodiscard]] constexpr bool IsPalindrome(std::string_view const str) noexcept {
auto begin = str.begin();
auto mid = begin + str.size() / 2;
return std::equal(begin, mid, str.rbegin());
}
The usual idiom for writing a loop is to start at the beginning and stop when you've passed the last element. With an index it looks like this:
for (int i = 0; i < a.size(); ++i) {
// do something with a[i]
}
Note that the terminating condition becomes true when i goes off the end.
To do that when you want to go in the other direction you have two choices:
for (int i = a.size(); i > 0; --i) {
// do something with a[i - 1]
}
In this case, the element we're interested in is a[i - 1]. The loop starts with the topmost element, when i is a.size(), and stops after the last element, when i becomes 0 and accessing a[i - 1] would be off the end.
That's a bit tricky to visualize, so you might be tempted to write it like this instead:
for (int i = a.size() - 1; i >= 0; --i) {
// do something with a[i]
}
This version has a subtle bug, and should not be your usual approach. The problem is that a.size() is unsigned. When a is empty, a.size() is 0, and a.size() - 1 wraps around and becomes a very large value. So don't use it.
Another way to avoid problems like this is to use iterators, which handle the details of recognizing the beginning and end of the sequence:
for (auto iter = std::begin(a); iter != std::end(a); ++iter) {
// do something with *iter
}
This is easily adapted to reverse iteration:
for (auto iter = std::rbegin(a); iter != std::rend(a); ++iter) {
// do something with *iter
}
And yet another possibility is to let the standard library handle the iteration:
void do_someting_with_a_sub_i(int) {
// whatever
}
std::for_each(std::begin(a), std::end(a), do_someting_with_a_sub_i);
or, in reverse,
std::for_each(std::rbegin(a), std::rend(a), do_someting_with_a_sub_i);
(yes, modern fashion calls for using a lambda instead of writing out do_something_with_a_sub_i. Sigh.)
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.
I am using two dynamic arrays to read from a file. They are to keep track of each word and the amount of times it appears. If it has already appeared, I must keep track in one array and not add it into the other array since it already exists. However, I am getting blank spaces in my array when I meet a duplicate. I think its because my pointer continues to advance, but really it shouldn't. I do not know how to combat this. The only way I have was to use a continue; when I print out the results if the array content = ""; if (*(words + i) == "") continue;. This basically ignores those blanks in the array. But I think that is messy. I just want to figure out how to move the pointer back in this method. words and frequency are my dynamic arrays.
I would like guidance in what my problem is, rather than solutions.
I have now changed my outer loop to be a while loop, and only increment when I have found the word. Thank you WhozCraig and poljpocket.
Now this occurs.
Instead of incrementing your loop variable [i] every loop, you need to only increment it when a NEW word is found [i.e. not one already in the words array].
Also, you're wasting time in your inner loop by looping through your entire words array, since words will only exist up to index i.
int idx = 0;
while (file >> hold && idx < count) {
if (!valid_word(hold)) {
continue;
}
// You don't need to check past idx because you
// only have <idx> words so far.
for (int i = 0; i < idx; i++) {
if (toLower(words[i]) == toLower(hold)) {
frequency[i]++;
isFound = true;
break;
}
}
if (!isFound) {
words[idx] = hold;
frequency[idx] = 1;
idx++;
}
isFound = false;
}
First, to address your code, this is what it should probably look like. Note how we only increment i as we add words, and we only ever scan the words we've already added for duplicates. Note also how the first pass will skip the j-loop entirely and simply insert the first word with a frequency of 1.
void addWords(const std::string& fname, int count, string *words, int *frequency)
{
std::ifstream file(fname);
std::string hold;
int i = 0;
while (i < count && (file >> hold))
{
int j = 0;
for (; j<i; ++j)
{
if (toLower(words[j]) == toLower(hold))
{
// found a duplicate at j
++frequency[j];
break;
}
}
if (j == i)
{
// didn't find a duplicate
words[i] = hold;
frequency[i] = 1;
++i;
}
}
}
Second, to really address your code, this is what it should actually look like:
#include <iostream>
#include <fstream>
#include <map>
#include <string>
//
// Your implementation of toLower() goes here.
//
typedef std::map<std::string, unsigned int> WordMap;
WordMap addWords(const std::string& fname)
{
WordMap words;
std::ifstream inf(fname);
std::string word;
while (inf >> word)
++words[toLower(word)];
return words;
}
If it isn't obvious by now how a std::map<> makes this task easier, it never will be.
check out SEEK_CUR(). If you want to set the cursor back
The problem is a logical one, consider several situations:
Your algorithm does not find the current word. It is inserted at position i of your arrays.
Your algorithm does find the word. The frequency of the word is incremented along with i, which leaves you with blank entries in your arrays whenever there's a word which is already present.
To conclude, 1 works as expected but 2 doesn't.
My advice is that you don't rely on for loops to traverse the string but use a "get-next-until-end" approach which uses a while loop. With this, you can track your next insertion point and thus get rid of the blank entries.
int currentCount = 0;
while (file)
{
// your inner for loop
if (!found)
{
*(words + currentCount) = hold;
*(frequency + currentCount) = 1;
currentCount++;
}
}
Why not use a std::map?
void collect( std::string name, std::map<std::string,int> & freq ){
std::ifstream file;
file.open(name.c_str(), std::ifstream::in );
std::string word;
while( true ){
file >> word; // add toLower
if( file.eof() ) break;
freq[word]++;
}
file.close();
}
The problem with your solution is the use of count in the inner loop where you look for duplicates. You'll need another variable, say nocc, initially 0, used as limit in the inner loop and incremented whenever you add another word that hasn't been seen yet.
I'm trying to loop through a list of strings and find where a given character is located at in said string. I then store the string in a given vector based on where/if the character occurs. I'm getting a runtime error in the following code before the loop finishes executing. I've looked over the it half a dozen times already and can't seem to find anything wrong.
vector< vector<string> > p;
for(list< string >::iterator ix = dictionary.begin(); ix != dictionary.end(); ix++)
{
int index = contains(*ix, guess);
index++;
p.at(index).push_back(*ix); //0 will contain all the words that do not contain the letter
//1 will be the words that start with the char
//2 will be the words that contain the the char as the second letter
//etc...
}
int contains(string str, char c)
{
char *a = (char *)str.c_str();
for(int i = 0; i < (str.size() + 1); i++)
{
if(a[i] == c)
return i;
}
return -1;
}
Change
(str.size() + 1)
...to
str.size()
You would be in undefined territory at str.size(), let alone that PLUS one.
For that matter, why are you fiddling with the extra char* instead of std::string[]?
For THAT matter, why don't you simply use std::string::find()?
That is, of course, assuming you're using std::string and not some other string... :)
In fact, back to the call site... string::find() returns the index of where the target character matched, or string::npos if NOT matched. So, can you dispense with the extra function altogether?
int pos = (*ix).find( guess );
p.at( ( pos == string::npos ) ? 0 : ( pos + 1 ) ).push_back( *ix );
vector< vector > p defines p as empty vector. You must have vector elements added to it before using vector::at().
For example:
const size_t MAX_LETTERS_IN_WORD = 30;
vector< vector<string> > p(MAX_LETTERS_IN_WORD);
/* same as before */
As an alternative you can check p.size() before using at() and push_back() additional elements into p as needed
The problem with the runtime error, might be because you access the vector p at a position that doesn't exist yet. You have to make space in the vector before you access a specific index.