I am trying to grab all substrings in between or after the occurrences of a certain character.
Specifically with for search query urls (grabbing the options), for example if I have:
std::string url = "https://www.google.com/search?q=i+need+help&rlz=1C1CHBF_enUS851US851&oq=i+need+help&aqs=chrome.0.69i59j0l3j69i60l2.4646j0j7&sourceid=chrome&ie=UTF-8"
I need to output the strings in between and after (for the last occurrence) the "&" character
so the output would be:
rlz=1C1CHBF_enUS851US851
oq=i+need+help
aqs=chrome.0.69i59j0l3j69i60l2.4646j0j7
sourceid=chrome
ie=UTF-8
I understand how to do this with one string, but I am stuck trying to implement it into a loop. This has to be done with several urls of different lengths and number of options.
So far I can only grab one substring, between the first and second occurrences of character, but I need to grab all of them in any given url.
int a = url.find("&") + 1;
int b = url.find("&", url.find("&") + 1);
int c = (b - a);
std::string option = url.substr(a, c);
Just find the next & from the previous one in a loop and exit the loop if you cannot find any & more and take care of the first element:
vector<string> foo(const string& url)
{
vector<string> result;
auto a = url.find("?");
if (a == string::npos) return result;
auto b = url.find("&");
if (b == string::npos)
{
result.push_back(url.substr(a + 1, string::npos));
return result;
}
result.push_back(url.substr(a + 1, b - a - 1));
do
{
a = b;
b = url.find("&", a + 1);
result.push_back(url.substr(a + 1, b - a - 1));
} while (b != string::npos);
return result;
}
works for your example: https://ideone.com/SiRZQB
Tbh I genuinely think that you should use a proper URI parser for this job as there could be a lot of fringe cases. But here you go:
#include <iostream>
#include <string>
int main()
{
std::string url = "https://www.google.com/search?q=i+need+help&rlz=1C1CHBF_enUS851US851&oq=i+need+help&aqs=chrome.0.69i59j0l3j69i60l2.4646j0j7&sourceid=chrome&ie=UTF-8";
char delimiter = '&';
size_t start = url.find(delimiter);
size_t end;
while (start != std::string::npos) {
end = url.find(delimiter, start + 1);
std::cout << url.substr(start + 1, end - start - 1) << std::endl;
start = end;
}
}
Playground: http://cpp.sh/8pshy7
You can try the following code which uses the regular expressions to parse the url.
#include <regex>
#include <iostream>
#include <string>
using namespace std;
int main(){
string url = "https://www.google.com/search?q=i+need+help&rlz=1C1CHBF_enUS851US851&oq=i+need+help&aqs=chrome.0.69i59j0l3j69i60l2.4646j0j7&sourceid=chrome&ie=UTF-8";
regex rg("[\?&](([^&]+)=([^&]+))");
for(smatch sm; regex_search(url, sm, rg); url=sm.suffix())
cout << sm[1] <<endl;
return 0;
}
You can use just an ordinary for loop as for example
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
int main()
{
std::string url = "https://www.google.com/search?q=i+need+help"
"&rlz=1C1CHBF_enUS851US851"
"&oq=i+need+help"
"&aqs=chrome.0.69i59j0l3j69i60l2.4646j0j7"
"&sourceid=chrome&ie=UTF-8";
char c = '&';
size_t n = std::count_if( std::begin( url ), std::end( url ),
[=]( const auto &item )
{
return item == c;
} );
std::vector<std::string> v;
v.reserve( n );
for ( auto pos = url.find( c, 0 ); pos != std::string::npos; )
{
auto next = url.find( c, ++pos );
auto n = ( next == std::string::npos ? url.size() : next ) - pos;
v.push_back( url.substr( pos, n ) );
pos = next;
}
for ( const auto &s : v ) std::cout << s << '\n';
}
The program output is
rlz=1C1CHBF_enUS851US851
oq=i+need+help
aqs=chrome.0.69i59j0l3j69i60l2.4646j0j7
sourceid=chrome
ie=UTF-8
Or you can write a separate function as for example
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
std::vector<std::string> split_url( const std::string &url, char c = '&' )
{
size_t n = std::count_if( std::begin( url ), std::end( url ),
[=]( const auto &item )
{
return item == c;
} );
std::vector<std::string> v;
v.reserve( n );
for ( auto pos = url.find( c, 0 ); pos != std::string::npos; )
{
auto next = url.find( c, ++pos );
auto n = ( next == std::string::npos ? url.size() : next ) - pos;
v.push_back( url.substr( pos, n ) );
pos = next;
}
return v;
}
int main()
{
std::string url = "https://www.google.com/search?q=i+need+help"
"&rlz=1C1CHBF_enUS851US851"
"&oq=i+need+help"
"&aqs=chrome.0.69i59j0l3j69i60l2.4646j0j7"
"&sourceid=chrome&ie=UTF-8";
auto v = split_url(url );
for ( const auto &s : v ) std::cout << s << '\n';
}
Related
For example I have vector {'a','a','b','b','c'} and I want to get the most letters which is a and b but this code the output is a;
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
int getMostFrequentElement(std::vector<char> &arr)
{
if (arr.empty())
return -1;
std::sort(arr.begin(), arr.end());
auto last_int = arr.front();
auto most_freq_int = arr.front();
int max_freq = 0, current_freq = 0;
for (const auto &i : arr) {
if (i == last_int)
++current_freq;
else {
if (current_freq > max_freq) {
max_freq = current_freq;
most_freq_int = last_int;
}
last_int = i;
current_freq = 1;
}
}
if (current_freq > max_freq) {
max_freq = current_freq;
most_freq_int = last_int;
}
return most_freq_int;
}
int main(){
std::vector<char> arr = {'a','a','b','b','c'};
char ret = getMostFrequentElement(arr);
std::cout << "Most frequent element = " << ret;
}
May I know why my output becomes a instead a and b?
input vector arr{'a','a','b','b','c'}
expected output is a and b
but my output is a
Your function returns only the first most frequent character as an integer in a sorted vector.
For starters the implementation of the function is not good. The function shall not sort the passed by reference vector. It is the owner of the vector decides whether to sort the vector before calling the function. The function shall not modify the passed to it vector.
If you want that the function would return all most frequent characters in a vector then you need to change the function essentially.
For example the function can look the following way as it is shown in the demonstrative program below.
#include <iostream>
#include <vector>
#include <map>
#include <iterator>
#include <algorithm>
std::vector<char> getMostFrequentElement( const std::vector<char> &v )
{
std::vector<char> result;
std::map<char, size_t> m;
for ( const auto &c : v ) ++m[c];
auto it = std::max_element( std::begin( m ), std::end( m ),
[]( const auto &p1, const auto &p2 )
{
return p1.second < p2.second;
} );
if ( it != std::end( m ) )
{
for ( const auto &p : m )
{
if ( p.second == it->second ) result.push_back( p.first );
}
}
return result;
}
int main()
{
std::vector<char> v = { 'a', 'a', 'b', 'b', 'c' };
auto result = getMostFrequentElement( v );
for ( const auto &c : result ) std::cout << c << ' ';
std::cout << '\n';
return 0;
}
The program output is
a b
The answer from Vlad is good and should be accepted.
I would like to show an additional, more "mordern" C++ solution.
The Function body is rather compact and consists only of 3 lines of code. It will count all occurences of char and sort it in decreasing order regarding the occurence.
So, the caller of this function can show all kind of information. In the example below, I show all topmost elements.
But all kind of other evaluations may be shown.
Please see:
#include <iostream>
#include <vector>
#include <utility>
#include <algorithm>
#include <set>
#include <iterator>
#include <unordered_map>
// Writing some aliases to prevent later typing work and make the code a little bit more readable. ---------------------
using DataType = char;
using CounterType = unsigned int;
using Pair = std::pair<DataType, CounterType>;
using Counter = std::unordered_map<DataType, CounterType>;
using Data = std::vector<DataType>;
struct Comp { bool operator ()(const Pair& p1, const Pair& p2) const { return (p1.second == p2.second) ? p1.first<p2.first : p1.second>p2.second; } };
using CountedAndSorted = std::multiset<Pair, Comp>;
// ----------------------------------------------------------------------------------------------------------------------
CountedAndSorted getMostFrequentElement(Data& data) {
// Count
Counter counter{};
for (const char c : data) counter[c]++;
// Return counted and sorted result
return {counter.begin(), counter.end()};
}
// ------------------------
// Test/Driver code
int main() {
// Test Data
Data d = { 'a', 'a', 'b', 'b', 'c' };
// Calculate result
auto result = getMostFrequentElement(d);
// Show output
for (const auto& [c, count] : result) if (count == result.begin()->second) std::cout << c << ' ';
}
I have a cpp vector containing separate words and I need to count how many times a word appears using a list. I try to iterate through the list but failing with the comparison of the two STL containers, whether the following word is already in my list or not. If not, I want to add that word to my list with an appearance of 1. I have a struct that counts the times a word appeared in the text.
The following code returns a list of words and numbers, but not each in my vector and I can't see why.
struct counter{
string word;
int sum = 1;
counter(){};
counter(string word): word(word){};
};
list<counter> list_count(vector<string> &text){
list<counter> word_count;
list<counter>::iterator it = word_count.begin();
for(string t:text){
if(it != word_count.end()){
it -> sum++;
} else {
word_count.push_back(counter(t));
}
++it;
}
return word_count;
}
Thank you in advance.
list<counter> list_count(const vector<string>& text) {
list<counter> word_count;
for (const string& t : text) {
auto it = std::find_if(word_count.begin(), word_count.end(),
[&](const counter& c){ return c.word == t; });
if (it != word_count.end()) {
it -> sum++;
} else {
word_count.push_back(counter(t));
}
}
return word_count;
}
Untested code.
You are not actually searching the std::list at all. On every loop iteration through the std::vector, you need to search the entire std::list from front to back, eg:
#include <string>
#include <list>
#include <vector>
#include <algorithm>
using namespace std;
struct counter {
string word;
int sum = 1;
counter(const string &word): word(word) {}
};
list<counter> list_count(const vector<string> &text) {
list<counter> word_count;
for(const string &t: text) {
// perform an actual search here!
list<counter>::iterator it = find_if(
word_count.begin(), word_count.end(),
[&](counter &c){ return (c.word == t); }
);
if (it != word_count.end()) {
it->sum++;
} else {
word_count.emplace_back(t);
}
}
return word_count;
}
Live Demo
That being said, a std::list is a poor solution for counting elements. A better solution is to use a std::(unordered_)map instead (unless you need to preserve the order of the words found, which neither one will do), eg:
#include <string>
#include <map>
#include <vector>
using namespace std;
map<string, int> list_count(const vector<string> &text) {
map<string, int> word_count;
for(const string &t: text) {
word_count[t]++;
}
return word_count;
}
Live Demo (using std::map)
Live Demo (using std::unordered_map)
You are trying to use an inefficient approach. The standard class template list does not have random access to its elements. Each new element is appended to the end of the list. To find whether an element is already present in the list elements of it are traversed sequentially.
It would be much efficiently to use the standard container std::map . Moreover in this container words will be ordered.
For example you could declare
std::map<std::string, size_t> counters;
Nevertheless if you want to use the list then the function can look as it is shown in the demonstrative program below.
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <iterator>
#include <algorithm>
struct counter
{
std::string word;
size_t n = 0;
counter() = default;
counter( const std::string &word ): word( word ), n( 1 ){}
};
std::list<counter> list_count( const std::vector<std::string> &text )
{
std::list<counter> word_count;
for ( const auto &s : text )
{
auto it = std::find_if( std::begin( word_count ), std::end( word_count ),
[&s]( const auto &c ) { return c.word == s; } );
if ( it == std::end( word_count ) )
{
word_count.push_back( s );
}
else
{
++it->n;
}
}
return word_count;
}
int main()
{
std::vector<std::string> v { "first", "second", "first" };
auto word_count = list_count( v );
for ( const auto &c : word_count )
{
std::cout << c.word << ": " << c.n << '\n';
}
return 0;
}
Its output is
first: 2
second: 1
Pay attention to that the definition of the struct counter is redundant. You could use instead the standard class std::pair. Here you are.
#include <iostream>
#include <string>
#include <utility>
#include <list>
#include <vector>
#include <iterator>
#include <algorithm>
std::list<std::pair<std::string, size_t>> list_count( const std::vector<std::string> &text )
{
std::list<std::pair<std::string, size_t>> word_count;
for ( const auto &s : text )
{
auto it = std::find_if( std::begin( word_count ), std::end( word_count ),
[&s]( const auto &p ) { return p.first == s; } );
if ( it == std::end( word_count ) )
{
word_count.emplace_back( s, 1 );
}
else
{
++it->second;
}
}
return word_count;
}
int main()
{
std::vector<std::string> v { "first", "second", "first" };
auto word_count = list_count( v );
for ( const auto &p : word_count )
{
std::cout << p.first << ": " << p.second << '\n';
}
return 0;
}
If to use std::map then the function will look very simple.
#include <iostream>
#include <string>
#include <vector>
#include <map>
std::map<std::string, size_t> list_count( const std::vector<std::string> &text )
{
std::map<std::string, size_t> word_count;
for ( const auto &s : text )
{
++word_count[s];
}
return word_count;
}
int main()
{
std::vector<std::string> v { "first", "second", "first" };
auto word_count = list_count( v );
for ( const auto &p : word_count )
{
std::cout << p.first << ": " << p.second << '\n';
}
return 0;
}
Using of the list will be efficient only in the case when the vector of strings is sorted.
Here is a demonstrative program.
#include <iostream>
#include <string>
#include <list>
#include <vector>
struct counter
{
std::string word;
size_t n = 0;
counter() = default;
counter( const std::string &word ): word( word ), n( 1 ){}
};
std::list<counter> list_count( const std::vector<std::string> &text )
{
std::list<counter> word_count;
for ( const auto &s : text )
{
if ( word_count.empty() || word_count.back().word != s )
{
word_count.push_back( s );
}
else
{
++word_count.back().n;
}
}
return word_count;
}
int main()
{
std::vector<std::string> v { "A", "B", "B", "C", "C", "C", "D", "D", "E" };
auto word_count = list_count( v );
for ( const auto &c : word_count )
{
std::cout << c.word << ": " << c.n << '\n';
}
return 0;
}
Its output is
A: 1
B: 2
C: 3
D: 2
E: 1
Could we replace a loop with a strcmp by something easier to read, something similar to the C# extension methods?
I maintain legacy C++ and wonder how to start a transition to a more modern C++ and there is profusion of code that looks like this one:
int numberOfPipe = 10;
char* collection[5] = { "pompe","pipe","turbine","pompe", "pipe" };
// Count the pipes in the collection
int nPipeFound = 0;
int nPipe = 5;
for (int idx = 0; idx < nPipe; idx++)
{
if (strcmp(collection[idx], "pipe") == 0)
nPipeFound++;
}
cout << nPipeFound << endl;
Use the standard library:
Use std::count and use the std::string comparison.
#include <algorithm>
#include <iostream>
#include <string>
int main() {
char const * collection[] = { "pompe","pipe","turbine","pompe","pipe" };
auto n_pipe_found = std::count( std::begin( collection ), std::end( collection ), std::string{"pipe"});
std::cout << n_pipe_found << '\n';
}
Use std::count_if and write a predicate which does C string comparison for you.
#include <algorithm>
#include <cstring>
#include <iostream>
int main() {
char const * collection[] = { "pompe","pipe","turbine","pompe","pipe" };
auto n_pipe_found = std::count_if( std::begin( collection ), std::end( collection ),
[](char const * a) { return std::strcmp(a,"pipe") == 0; } );
std::cout << n_pipe_found << '\n';
}
You could also use a predicate like [](std::string const& a) { return a == "pipe"; } and again make use of std::string comparison.
Use std::accumulate if you need more fine grained control over counting.
#include <numeric>
#include <iostream>
#include <string>
int main() {
char const * collection[] = { "pompe","pipe","turbine","pompe","pipe" };
auto n_pipe_found = std::accumulate( std::begin( collection ), std::end( collection ), int{0},
[](int a, std::string const& b) { return a + (b == "pipe"); });
std::cout << n_pipe_found << '\n';
}
I need to implement a function in C++,
vector<string> generateSubstrings(string s),
that returns a vector of all substrings of a string. For example, the substrings of the string “rum” are the seven strings
“r”, “ru”, “rum”, “u”, “um”, “m”, “”.
The function has to be recursive and has to return the results as a vector.
Here is my code so far. It's only printing "r", "ru" and "rm". I'm having alot of trouble implementing this function. I've been working on this for the past few hours but I just can't figure out how to get it working as stated, so any help would be appreciated.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
vector<string> generateSubstrings(string s, int num){
int index = num;
int SIZE = s.size();
vector<string> substrings;
if(index == s.size()){//BASE CASE
string temp = s.substr(index,1);
substrings.push_back(temp);
}
else{
for(int i = 0; i < SIZE; ++i){
string temp = s.at(index) + s.substr(i,i);
substrings.push_back(temp);
}
generateSubstrings(s, num + 1);
}
return substrings;
}
int main() {
vector<string> vec(20);
vec = generateSubstrings("rum", 0);
cout << endl << endl;cout << "PRINTING VECTOR" << endl;
for ( int i = 0; i<vec.size();++i){
cout << vec.at(i);
cout << endl;
}
cout << "DONE";
}
In your assignment there is written that the recursive function has to be declared like
vector<string> generateSubstrings(string s),
But you are trying to make another function recursive that declared like
vector<string> generateSubstrings(string s, int num);
So in any case your solution does not satisfy the requirement of the assignment.
The function can look the following way
#include <iostream>
#include <string>
#include <vector>
std::vector<std::string> generateSubstrings( std::string s )
{
if ( s.empty() ) return {};
std::vector<std::string> v;
v.reserve( s.size() * ( s.size() + 1 ) / 2 );
for ( std::string::size_type i = 0; i < s.size(); i++ )
{
v.push_back( s.substr( 0, i + 1 ) );
}
for ( const std::string &t : generateSubstrings( s.substr( 1 ) ) )
{
v.push_back( t );
}
return v;
}
int main()
{
std::string s( "rum" );
for ( const std::string &t : generateSubstrings( s ) )
{
std::cout << t << std::endl;
}
return 0;
}
Its output is
r
ru
rum
u
um
m
If you need also to include an empty string then you should change condition
if ( s.empty() ) return {};
in appropriate way. For example
if ( s.empty() ) return { "" };
Also in this case you should write
v.reserve( s.size() * ( s.size() + 1 ) / 2 + 1 );
Also you can replace the loop in the shown function with method insert. For example
#include <iostream>
#include <string>
#include <vector>
std::vector<std::string> generateSubstrings( std::string s )
{
if ( s.empty() ) return {};
std::vector<std::string> v;
v.reserve( s.size() * ( s.size() + 1 ) / 2 );
for ( std::string::size_type i = 0; i < s.size(); i++ )
{
v.push_back( s.substr( 0, i + 1 ) );
}
std::vector<std::string> v2 = generateSubstrings( s.substr( 1 ) );
v.insert( v.end(), v2.begin(), v2.end() );
return v;
}
int main()
{
std::string s( "rum" );
for ( const std::string &t : generateSubstrings( s ) )
{
std::cout << t << std::endl;
}
return 0;
}
The program output will be the same as shown above.
Here is a program modification that includes an empty string in the vector.
#include <iostream>
#include <string>
#include <vector>
std::vector<std::string> generateSubstrings( std::string s )
{
if ( s.empty() ) return { "" };
std::vector<std::string> v;
v.reserve( s.size() * ( s.size() + 1 ) / 2 + 1 );
for ( std::string::size_type i = 0; i < s.size(); i++ )
{
v.push_back( s.substr( 0, i + 1 ) );
}
std::vector<std::string> v2 = generateSubstrings( s.substr( 1 ) );
v.insert( v.end(), v2.begin(), v2.end() );
return v;
}
int main()
{
std::string s( "rum" );
for ( const std::string &t : generateSubstrings( s ) )
{
std::cout << t << std::endl;
}
return 0;
}
Here's an answer using Python. It prints the correct result for "rum", but for "rumm" it prints two "m" substrings for obvious reasons:
def substrings(s):
result = []
if len(s) == 0:
result.append("")
if len(s) > 0:
result += substrings(s[1:])
for n in range(1,len(s)+1):
result.append(s[0:n])
return result
print substrings("rum")
print substrings("rumm")
The idea of the algorithm is the following: for "rum", the substrings are the substrings of "um" followed by "r", "ru" and "rum". For "um", the substrings are the substrings of "m" followed by "u" and "um". For "m", the substrings are the substrings of "" followed by "m". For "", the substrings are simply "". So, the final list is "", "m", "u", "um", "r", "ru", "rum".
Although this isn't C++, you should be able to translate the code to C++. But that may not necessarily be what you want as "rumm" has two "m" substrings. If you think that "rumm" should have only one "m" substring, please leave a comment and I'll post another answer.
First, you should pay attention about code indent.
Then, I don't look your code, I wrote some code to achieve your aim, as follow:
void generateSubstrings(string s, int num, vector<string> &sta)
{
if (num == s.size())
return;
auto b = begin(s) + num;
string temp = "";
temp += *b;
sta.push_back(temp);
b++;
while (b != end(s))
{
temp += *b;
sta.push_back(temp);
b++;
}
generateSubstrings(s, num + 1, sta);
}
So i am extremely new to this. I have an assignment to count the number of lines, words, characters, unique lines and unique words from user input. So far I have gotten lines, unique lines and characters from my code. I thought I got the words but then it doesn't work when i factor in double spaces and tabs. Also i have no clue how to find the unique words. Please offer your assistance.
Code:
// What I dont have:
//words
//Total words
#include <iostream>
#include <string>
#include <set>
using namespace std;
unsigned long countWords(const string& s, set<string>& wl); //total words
int main()
{
int linenum=0, charnum=0, totalwords=0;
set<string> lines;
string input;
set<string> unique; //to store unique words from countWords function
while (getline(cin,input))
{
lines.insert(input);
linenum++;
charnum+= input.length();
totalwords += countWords(input,unique);
}
cout << linenum <<" "<< totalwords <<" "<< charnum <<" " << lines.size()<<" " << unique.size()<< endl;
system("PAUSE");
return 0;
}
unsigned long countWords(const string& s, set<string>& wl) //total words
{
int wcount=1;
for (unsigned int i=0; i < s.length(); i++)
{
if ((s.at(i) == ' ')&&(s.at(i)+1 !='\0')) {
wcount++;
}
}
return wcount;
}
you need to put +1 inside brackets,your function will be like that
unsigned long countWords(const string& s, set<string>& wl) //total words
{
int wcount=0;// initial value must be zero
int N = 0;// you need to add this to count the characters of each word.
for (unsigned int i=0; i < s.length(); i++)
{
if ((s.at(i) == ' ')||(s.at(i+1) =='\0')) {// Condition must be or instead of and
wl.insert(s.substr(i-N-1,N));
++wcount;
N = 0;
}else ++N;
}
return wcount;
}
Here is an example of how the function could look
#include <iostream>
#include <sstream>
#include <set>
#include <string>
#include <iterator>
#include <algorithm>
unsigned long countWords( std::set<string> &wl, const std::string &s )
{
std::istringstream is( s );
wl.insert( std::istream_iterator<std::string>( is ),
std::istream_iterator<std::string>() );
is.clear();
is.str( s );
return ( std::distance( std::istream_iterator<std::string>( is ),
std::istream_iterator<std::string>() ) );
}
//...
In this example puctuations are considered as parts of words.
If you do not know yet std::istringstream and other facilities of C++ then you can write the function the following way
#include <iostream>
#include <set>
#include <string>
unsigned long countWords( std::set<string> &wl, const std::string &s )
{
const char *white_space = " \t";
unsigned long count = 0;
for ( std::string::size_type pos = 0, n = 0;
( pos = s.find_first_not_of( white_space, pos ) ) != std::string::npos;
pos = n == std::string::npos ? s.size() : n )
{
++count;
n = s.find_first_of( white_space, pos );
wl.insert( s.substr( pos, ( n == std::string::npos ? std::string::npos : n - pos ) ) );
}
return count;
}
//...