c++ search for string in an vector of strings - c++

I am working on a project to read a Context-free Grammar and represent it 1.Vectorial, 2. Branched chained lists, 3. Array (Table). I encounter a problem with string comparison. When I read a string from keyboard representing right side of a Production rule, I want to check if that string exists in a vector of strings. The problem is that comparison is not working right. If the string I compare is the first string from the vector of strings than the comparison is working fine. But if the string I compare is a string from the vector of strings other than first comparison is not working, it's like the string is not from the vector of strings. Sorry for my English. I better let the code explain
bool is(string &s, vector<string> &v) {
for (auto i : v) {
return (i.compare(s)==0) ? true : false;
}
}
This function returns true only if s=v[0], otherwise returns false even if s=v[2]

To do that you'd have to loop into the vector with a for loop and compare every string in it, it would be something like:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
bool doExists(string s, vector<string> v) {
for (int i=0;i<v.size();i++) {
if (s.compare(v[i]) == 0) {
return true;
}
}
return false;
}
int main(){
vector<string> phrases = {"Hello!", "I like potatos!","I like fries.","How are you today?","I'm good.","Hello!"};
int helloLocated = doExists("Hello!", phrases);
cout << helloLocated;
}
The console would print 1.

Related

How to capitalize the first letter of each name in an array?

Here is the question:
Create a function that takes an array of names and returns an array where only the first letter of each name is capitalized.
example
capMe(["mavis", "senaida", "letty"]) âžž ["Mavis", "Senaida", "Letty"]
And the code I wrote to answer this question:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void capme(vector<string> name)
{
char ch;
for(int i = 0; i < name[i].size(); i++)
{
putchar(toupper(name[i][0]));
cout << name[i] << endl;
}
}
int main()
{
vector <string> name = {"mavis", "senaida", "letty"};
capme(name);
return 0;
}
As you can see, it prints "Mmavis", "Ssenaida", "Lletty", which is wrong. Can you guys help me in answering this question as I don't know how?
To change the input argument, we have two choice: make the argument mutable reference, or add a return type, here I choose the first one.
putchar can be used to print only one character, it recommended to use cout to print a string, possible solutions:
with traditional loop: capme
with range for-loop since c++11 : capme2
with stl algorithm transform: capme3
Don't forget to check if the string element is empty, or you may crash while accessing the first character.
To obey the single-responsibility principle (SRP), it's better to print the string vector out of the capme function.
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void capme(vector<string>& name) {
for (int i = 0; i < name[i].size(); i++) {
if (name[i].empty()) continue;
name[i][0] = toupper(name[i][0]);
}
}
void capme2(vector<string>& names) {
for (auto& name : names) {
if (name.empty()) continue;
name[0] = toupper(name[0]);
}
}
void capme3(vector<string>& names) {
std::transform(names.begin(), names.end(), names.begin(), [](auto& s) {
return s.empty() ? s : (s[0] = toupper(s[0]), s);
});
}
Online demo
You have used the wrong function. What you need is a replacement and not a prepend. Try using std::string::operator[] to access the first element of the words in the vector. This is how I would write this code:
std::vector<string> capitaliseInitLetter(std::vector<string> vec) {
for (auto& word : vec) {
word[0] -= 32; //add a check to see if the letter is already capital
}
return vec;
}
The above code is just an example which assumes that the input is valid. You'll have to add checks and exception handling for invalid inputs on your own. (Hint: Take a look at #prehistoricpenguin's answer)
You are calling putchar() which writes a character to standard output, and in this case is printing the first letter of each string in name as uppercase, then you are writing the entire string to standard output immediately after.
Additionally, your function does not meet the requirements you stated above saying it should return an array where the strings have the first letter capitalized.
What you could do is change the signature of capme() to return a std::vector<std::string>, and perhaps utilize the for_each() function to handle changing the first letter of each string in your vector then return it.
For reference:
#include <vector>
#include <string>
#include <algorithm>
#include <cctype>
std::vector<std::string> capme(std::vector<std::string> name)
{
std::for_each(name.begin(), name.end(), [](std::string &s) {
s[0] = toupper(s[0]);
});
return name;
}
Or as kesarling suggested, a simple for each loop:
std::vector<std::string> capme(std::vector<std::string> name)
{
for (auto& s : name) {
s[0] = toupper(s[0]);
}
return name;
}

How to check if a string ends with 'ed' in C++;

How to write a program that reads 5 strings from user input and prints only those strings that end with the letter ‘ed’ in C++. Need help!
The solution is rather straightforward.
First we define a container that can contain 5 std::string. For that we use a std::vector together with a constructor to reserve space for the 5 elements.
Then we copy 5 strings from the console (from user input) into the vector.
And, last, we copy elements out of the std::vector to std::cout, if the strings end with "ed".
Because of the simplicity of the program, I cannot explain much more . . .
Please see.
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <iterator>
constexpr size_t NumberOfTexts = 5U;
int main()
{
// Define a container that can hold 5 strings
std::vector<std::string> text(NumberOfTexts);
// Read 5 strings from user
std::copy_n(std::istream_iterator<std::string>(std::cin), NumberOfTexts, text.begin());
// Print the strings with ending "ed" to display
std::copy_if(text.begin(), text.end(), std::ostream_iterator<std::string>(std::cout,"\n"), [](const std::string& s){
return s.size()>=2 && s.substr(s.size()-2) == "ed";
});
return 0;
}
Simple solution,
#include<iostream>
using namespace std;
bool endsWith(const std::string &mainStr, const std::string &toMatch)
{
if(mainStr.size() >= toMatch.size() &&
mainStr.compare(mainStr.size() - toMatch.size(), toMatch.size(), toMatch) == 0)
return true;
else
return false;
}
int main()
{
string s[5];
for(int i=0;i<5;i++)
{
cin>>s[i];
}
for(int i=0;i<5;i++)
{
if(endsWith(s[i],"ed"))
cout<<s[i]<<endl;
}
}
Hope This might Helps:)

remove chars from string in c++

I was implementing a method to remove certain characters from a string txt, in-place. the following is my code. The result is expected as "bdeg". however the result is "bdegfg", which seems the null terminator is not set. the weird thing is that when I use gdb to debug, after setting null terminator
(gdb) p txt
$5 = (std::string &) #0xbffff248: {static npos = <optimized out>,
_M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x804b014 "bdeg"}}
it looks right to me. So what is the problem here?
#include <iostream>
#include <string>
using namespace std;
void censorString(string &txt, string rem)
{
// create look-up table
bool lut[256]={false};
for (int i=0; i<rem.size(); i++)
{
lut[rem[i]] = true;
}
int i=0;
int j=0;
// iterate txt to remove chars
for (i=0, j=0; i<txt.size(); i++)
{
if (!lut[txt[i]]){
txt[j]=txt[i];
j++;
}
}
// set null-terminator
txt[j]='\0';
}
int main(){
string txt="abcdefg";
censorString(txt, "acf");
// expect: "bdeg"
std::cout << txt <<endl;
}
follow-up question:
if string is not truncated like c string. so what happens with txt[j]='\0'
and why it is "bdegfg" not 'bdeg'\0'g' or some corrupted strings.
another follow-up:
if I use txt.erase(txt.begin()+j, txt.end());
it works fine. so I'd better use string related api. the point is that I do not know the time complexity of the underlying code of these api.
std::string is not null terminated as you think therefore you have to use other ways to do this
modify the function to:
void censorString(string &txt, string rem)
{
// create look-up table
bool lut[256]={false};
for (int i=0; i<rem.size(); i++)
{
lut[rem[i]] = true;
}
// iterate txt to remove chars
for (std::string::iterator it=txt.begin();it!=txt.end();)
{
if(lut[*it]){
it=txt.erase(it);//erase the character pointed by it and returns the iterator to next character
continue;
}
//increment iterator here to avoid increment after erasing the character
it++;
}
}
Here basically you have to use std::string::erase function to erase any character in the string which take iterator as input and return iterator to next character
http://en.cppreference.com/w/cpp/string/basic_string/erase
http://www.cplusplus.com/reference/string/string/erase/
the complexity of erase function is O(n). So the whole function would have complexity of o(n^2). space complexity for a very long string i.e. >256 chars would be O(n).
Well there is another way which will have only O(n) complexity for time.
create a another string and append the character while iterating over the txt string which are not censored.
The new function would be:
void censorString(string &txt, string rem)
{
// create look-up set
std::unordered_set<char> luckUpSet(rem.begin(),rem.end());
std::string newString;
// iterate txt to remove chars
for (std::string::iterator it=txt.begin();it!=txt.end();it++)
{
if(luckUpSet.find(*it)==luckUpSet.end()){
newString.push_back(*it);
}
}
txt=std::move(newString);
}
Now this function has complexity of O(n), since functionstd::unordered_set::find and std::string::push_back have complexity of O(1).
if You use normal std::set find which has complexity of O(log n), then complexity of whole function would become O(n log n).
Embedding null-terminators inside a std::string is completely valid and will not change the length of the string. It will give you unexpected results if you, for example, try to output it using a stream extraction, though.
The goal you are attempting to reach can be done much easier:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
int main()
{
std::string txt="abcdefg";
std::string filter = "acf";
txt.erase(std::remove_if(txt.begin(), txt.end(), [&](char c)
{
return std::find(filter.begin(), filter.end(), c) != filter.end();
}), txt.end());
// expect: "bdeg"
std::cout << txt << std::endl;
}
In the same vein as Himanshu's answer, you can accomplish an O(N) complexity (using additional memory) like so:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
#include <unordered_set>
int main()
{
std::string txt="abcdefg";
std::string filter = "acf";
std::unordered_set<char> filter_set(filter.begin(), filter.end());
std::string output;
std::copy_if(txt.begin(), txt.end(), std::back_inserter(output), [&](char c)
{
return filter_set.find(c) == filter_set.end();
});
// expect: "bdeg"
std::cout << output << std::endl;
}
You have not told the string that you have changed it's size. You need to use the resize method to update the size if you remove any characters from the string.
Problem is you can't treat the C++ string like a C style string is the problem. I.e. you can't just insert a 0 like in C. To convince your self of this, add this to your code "cout << txt.length() << endl;" - you'll get 7. You want to use the erase() method;
Removes specified characters from the string.
1) Removes min(count, size() - index) characters starting at index.
2) Removes the character at position.
3) Removes the character in the range [first; last).
Text is a string not a character array.
This code
// set null-terminator
txt[j]='\0';
Will not truncate the string at the j-th position.

C++ Finding Anagrams in words

I'm working on a program that looks at whether or not a particular word is an anagram using std:count however, I don't think my function logic is correct and I cannot seem to figure it out.
Assume there are the following words in the file:
Evil
Vile
Veil
Live
My code is as follows:
#include <iostream>
#include <vector>
#include <fstream>
#include <map>
using namespace std;
struct Compare {
std::string str;
Compare(const std::string& str) : str(str) {}
};
bool operator==(const std::pair<int, std::string>&p, const Compare& c) {
return c.str == p.second;
}
bool operator==(const Compare& c, const std::pair<int, std::string>&p) {
return c.str == p.second;
}
std::vector<std::string> readInput(ifstream& file)
{
std::vector<std::string> temp;
string word;
while (file >> word)
{
temp.push_back(word);
}
std::sort(temp.begin(), temp.end());
return temp;
}
int main(int argc, char *argv[]) {
string file = "testing.txt";
ifstream ss(file.c_str());
if(!ss.is_open())
{
cerr << "Cannot open the text file";
}
std::vector<std::string> words = readInput(ss);
std::map<int, std::string> wordsMap;
//std::map<std::string value, int key> values;
for(unsigned i=0; (i < words.size()); i++)
{
wordsMap[i] = words[i];
}
int count = std::count(wordsMap.begin(), wordsMap.end(), Compare("Evil"));
cout << count << endl;
}
I'm pretty sure it's just a case of my logic is wrong in the functions. I hope someone can help :)
The most simple approach would be
To check like following (pseudo code)
bool isAnagram(string s, string t) {return sort(s) == sort(t); }
So, use some think like following, no need of std::map
struct Compare {
std::string str;
Compare(const std::string& x) : str(x) {
std::sort(str.begin(),str.end()); std::transform(str.begin(),
str.end(),str.begin(), ::toupper);}
bool operator ()(const std::string& t)
{
std::string s= t;
std::transform(s.begin(), s.end(),s.begin(), ::toupper);
std::sort(s.begin(),s.end());
return s == str;
}
};
And then
int count = std::count_if(words.begin(), words.end(), Compare("Evil"));
See HERE
This is not the most efficient algorithm, but a quick change to your program that would work could be:
bool operator==(const std::pair<int, std::string>&p, const Compare& c) {
std::string a = c.str;
std::transform(a.begin(), a.end(), a.begin(), ::tolower);
std::sort(a.begin(), a.end());
std::string b = p.second;
std::transform(b.begin(), b.end(), b.begin(), ::tolower);
std::sort(b.begin(), b.end());
return a == b;
}
EDIT: It seems in your present code, you are checking whether the strings are exactly equal to each other (not anagrams).
INSTEAD:
For each word, make an array of 26 elements, each element corresponding to a letter of the alphabet. Parse each word character by character, and increase the count of the particular character in the respective array.
For example for evil, the array would be:
0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0. // It has 1's for letters e,v,i and l
a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z
You make this array for each word that you have. In your case, all the words will have the same array. You then compare these arrays element-wise and proceed accordingly.
Now you just need to see which words have the same corresponding array.
If you want to compare all the N words pair-wise, you can do so using two nested loops in O(N^2) complexity.
The complexity for comparing one pair is O(1).
Complexity of creating the arrays = O(L) where L is the length of the string.
Consider the following:
map<string, set<string>> anagrams;
for (auto word : words)
anagrams[sort(word)].insert(word);
const set<string>& find_anagrams(const string& word)
{
return anagrams[word];
}
When you have a lot of words that are relatively short (or if you can work with large number libs), then you can use a solution similar to what I wrote here -
Generate same unique hash code for all anagrams
Essentially - map each character to a unique prime number (doesn't have to be big, you can map the entire ABC into primes up to 101), and for each word multiply the primes received from it characters. Since multiplication is commutative, anagrams would give the same result, so you just compare that result, hash it, or do whatever you want
Keep in mind that for long words the values would grow pretty fast, so you might need a big numbers lib

Ignoring several different words.. c++?

I'm reading in several documents, and indexing the words I read in. However, I want to ignore common case words (a, an, the, and, is, or, are, etc).
Is there a shortcut to doing this? Moreso than doing just...
if(word=="and" || word=="is" || etc etc....) ignore word;
For example, can I put them into a const string somehow, and have it just check against the string? Not sure... thank you!
Create a set<string> with the words that you would like to exclude, and use mySet.count(word) to determine if the word is in the set. If it is, the count will be 1; it will be 0 otherwise.
#include <iostream>
#include <set>
#include <string>
using namespace std;
int main() {
const char *words[] = {"a", "an", "the"};
set<string> wordSet(words, words+3);
cerr << wordSet.count("the") << endl;
cerr << wordSet.count("quick") << endl;
return 0;
}
You can use an array of strings, looping through and matching against each, or use a more optimal data structure such as a set, or trie.
Here's an example of how to do it with a normal array:
const char *commonWords[] = {"and", "is" ...};
int commonWordsLength = 2; // number of words in the array
for (int i = 0; i < commonWordsLength; ++i)
{
if (!strcmp(word, commonWords[i]))
{
//ignore word;
break;
}
}
Note that this example doesn't use the C++ STL, but you should.
If you want to maximize performance you should create a trie....
http://en.wikipedia.org/wiki/Trie
...of stopwords....
http://en.wikipedia.org/wiki/Stop_words
There is no standard C++ trie datastructure, however see this question for third party implementations...
Trie implementation
If you can't be bothered with that and want to use a standard container, the best one to use is unordered_set<string> which will put the stopwords in a hash table.
bool filter(const string& word)
{
static unordered_set<string> stopwords({"a", "an", "the"});
return !stopwords.count(word);
}