The question Goes like this ( my code in the last )
Reverse the given string word wise. That is, the last word in given string should come at 1st place, last second word at 2nd place and so on. Individual words should remain as it is.
Input format :
String in a single line
Output format :
Word wise reversed string in a single line
Constraints :
0 <= |S| <= 10^7
where |S| represents the length of string, S.
Sample Input 1:
Welcome to Coding Ninjas
Sample Output 1:
Ninjas Coding to Welcome
Sample Input 2:
Always indent your code
Sample Output 2:
code your indent Always
This code is in c++:
void reverseStringWordWise(char input[])
{
// Length
int count=0;
for(int i=0; input[i]!='\0'; i++)
{
count++;
}
int len=count;
//reversing the complete string
int i=0;
int j=len-1;
while(i<j)
{
char temp=input[i];
input[i]=input[j];
input[j]=temp;
i++;
j--;
}
//individual reverse
int k=0;
int a,b;
for(;k<len;)
{
for(;input[k]==' ';k++)
{
b=k-1;
break;
}
while(a<b)
{
char temp=input[a];
input[a]=input[b];
input[b]=temp;
}
}
}
can someone help me with the logic of reversing the individual word, c or c++ works.
I would get rid of the char[]s and use std::string.
Example:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
#include <list>
void reverseStringWordWise(std::string input) {
std::list<std::string> words;
for(auto sit = input.begin();;) {
// find a space from `sit` and forward
auto eit = std::find(sit, input.end(), ' ');
// store the word first in the list
words.emplace_front(sit, eit);
if(eit == input.end()) break; // last word, break out
sit = std::next(eit); // start next search after the found space
}
// print result
for(auto& word : words) std::cout << word << ' ';
std::cout << '\n';
}
int main() {
reverseStringWordWise("Hello world");
}
Output
world Hello
If you don't want the trailing space after the last word:
void reverseStringWordWise(std::string inp) {
std::list<std::string> words;
for (auto sit = inp.begin(), eit = sit; eit != inp.end(); sit = eit + 1) {
eit = std::find(sit, inp.end(), ' ');
words.emplace_front(sit, eit);
}
if(auto it = words.begin(); it != words.end()) {
std::cout << *it;
for(++it; it != words.end(); ++it) std::cout << ' ' << *it;
}
std::cout << '\n';
}
You can use the standard library algorithms to shorten the code. If you've got start and end iterators, you can use std::reverse, you can use std::strlen to calculate the end iterator and you can use std::find to identify the next word boundary. Assuming every word seperator is a space character, this could result in the following algorithm
void reverseStringWordWise(char input[])
{
if (input[0] == '\0')
{
return;
}
auto const end = input + std::strlen(input);
std::reverse(input, end);
auto wordEnd = input;
while(true)
{
auto wordStart = wordEnd;
wordEnd = std::find(wordStart, end, ' ');
std::reverse(wordStart, wordEnd);
if (wordEnd == end)
{
break;
}
++wordEnd;
}
}
int main() {
char input1[] = "Welcome to Coding Ninjas";
char input2[] = "Always indent your code";
reverseStringWordWise(input1);
reverseStringWordWise(input2);
std::cout << input1 << '\n'
<< input2 << '\n';
}
Here is another solution, using std::stack:
#include <stack>
#include <string>
#include <sstream>
#include <iostream>
void reverseStringWordWise(std::string input)
{
std::stack<std::string> wordStack;
std::istringstream strm(input);
std::string word;
// push each word on the stack
while (strm >> word)
wordStack.push(word);
// pop stack for each word
while (!wordStack.empty())
{
std::cout << wordStack.top() << ' ';
wordStack.pop();
}
}
int main()
{
reverseStringWordWise("Welcome to Coding Ninjas");
}
Output:
Ninjas Coding to Welcome
With boost, it's easier:
#include <boost/tokenizer.hpp>
#include <boost/algorithm/string/join.hpp>
std::string reverse_words(std::string s)
{
boost::char_delimiters_separator<char> const sep(" ");
boost::tokenizer<boost::char_delimiters_separator<char>> const words(s, sep);
return boost::algorithm::join(std::reverse(words.begin(), words.end()), " ");
}
In C++, you are expected to use algorithms and not bother with rewriting everything from scratch (unless you are writing a library such as boost). Especially, as others mentioned, reading/writing to arrays directly is most often going to end up with errors (your code is missing several boundary checks).
Here is a modern C++ version. The algorithm is to reverse each word and then reverse the whole string. The code takes the string by value so it has a copy of the string and then modifies the string in-place and returns it. It uses no extra memory.
#include <iostream>
#include <string>
#include <algorithm>
std::string word_reverse(std::string s) {
auto it = s.begin();
while(it != s.end()) {
auto it2 = std::find(it, s.end(), ' ');
std::reverse(it, it2);
it = it2 + (it2 != s.end());
}
std::reverse(s.begin(), s.end());
return s;
}
int main() {
std::string s = "Always indent your code";
std::string t = word_reverse(s);
std::cout << s << std::endl;
std::cout << t << std::endl;
}
Use Regex:-
// ```c++
#include <regex>
#include <iterator>
#include <iostream>
#include <string>
using it = std::regex_iterator<std::string::const_reverse_iterator>;
int main() {
const std::string s = "Always indent your code.";
std::regex regex("[\\w]+");
for (auto i = it(s.rbegin(), s.rend(), regex); i != it(); ++i) {
auto w = i->str();
std::copy(std::rbegin(w), std::rend(w), std::ostream_iterator<char>(std::cout));
std::cout << ' ';
}
}
Related
What would be easiest method to split a string using c++11?
I've seen the method used by this post, but I feel that there ought to be a less verbose way of doing it using the new standard.
Edit: I would like to have a vector<string> as a result and be able to delimitate on a single character.
std::regex_token_iterator performs generic tokenization based on a regex. It may or may not be overkill for doing simple splitting on a single character, but it works and is not too verbose:
std::vector<std::string> split(const string& input, const string& regex) {
// passing -1 as the submatch index parameter performs splitting
std::regex re(regex);
std::sregex_token_iterator
first{input.begin(), input.end(), re, -1},
last;
return {first, last};
}
Here is a (maybe less verbose) way to split string (based on the post you mentioned).
#include <string>
#include <sstream>
#include <vector>
std::vector<std::string> split(const std::string &s, char delim) {
std::stringstream ss(s);
std::string item;
std::vector<std::string> elems;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
// elems.push_back(std::move(item)); // if C++11 (based on comment from #mchiasson)
}
return elems;
}
Here's an example of splitting a string and populating a vector with the extracted elements using boost.
#include <boost/algorithm/string.hpp>
std::string my_input("A,B,EE");
std::vector<std::string> results;
boost::algorithm::split(results, my_input, boost::is_any_of(","));
assert(results[0] == "A");
assert(results[1] == "B");
assert(results[2] == "EE");
Another regex solution inspired by other answers but hopefully shorter and easier to read:
std::string s{"String to split here, and here, and here,..."};
std::regex regex{R"([\s,]+)"}; // split on space and comma
std::sregex_token_iterator it{s.begin(), s.end(), regex, -1};
std::vector<std::string> words{it, {}};
I don't know if this is less verbose, but it might be easier to grok for those more seasoned in dynamic languages such as javascript. The only C++11 features it uses is auto and range-based for loop.
#include <string>
#include <cctype>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
string s = "hello how are you won't you tell me your name";
vector<string> tokens;
string token;
for (const auto& c: s) {
if (!isspace(c))
token += c;
else {
if (token.length()) tokens.push_back(token);
token.clear();
}
}
if (token.length()) tokens.push_back(token);
return 0;
}
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
vector<string> split(const string& str, int delimiter(int) = ::isspace){
vector<string> result;
auto e=str.end();
auto i=str.begin();
while(i!=e){
i=find_if_not(i,e, delimiter);
if(i==e) break;
auto j=find_if(i,e, delimiter);
result.push_back(string(i,j));
i=j;
}
return result;
}
int main(){
string line;
getline(cin,line);
vector<string> result = split(line);
for(auto s: result){
cout<<s<<endl;
}
}
My choice is boost::tokenizer but I didn't have any heavy tasks and test with huge data.
Example from boost doc with lambda modification:
#include <iostream>
#include <boost/tokenizer.hpp>
#include <string>
#include <vector>
int main()
{
using namespace std;
using namespace boost;
string s = "This is, a test";
vector<string> v;
tokenizer<> tok(s);
for_each (tok.begin(), tok.end(), [&v](const string & s) { v.push_back(s); } );
// result 4 items: 1)This 2)is 3)a 4)test
return 0;
}
This is my answer. Verbose, readable and efficient.
std::vector<std::string> tokenize(const std::string& s, char c) {
auto end = s.cend();
auto start = end;
std::vector<std::string> v;
for( auto it = s.cbegin(); it != end; ++it ) {
if( *it != c ) {
if( start == end )
start = it;
continue;
}
if( start != end ) {
v.emplace_back(start, it);
start = end;
}
}
if( start != end )
v.emplace_back(start, end);
return v;
}
#include <string>
#include <vector>
#include <sstream>
inline vector<string> split(const string& s) {
vector<string> result;
istringstream iss(s);
for (string w; iss >> w; )
result.push_back(w);
return result;
}
Here is a C++11 solution that uses only std::string::find(). The delimiter can be any number of characters long. Parsed tokens are output via an output iterator, which is typically a std::back_inserter in my code.
I have not tested this with UTF-8, but I expect it should work as long as the input and delimiter are both valid UTF-8 strings.
#include <string>
template<class Iter>
Iter splitStrings(const std::string &s, const std::string &delim, Iter out)
{
if (delim.empty()) {
*out++ = s;
return out;
}
size_t a = 0, b = s.find(delim);
for ( ; b != std::string::npos;
a = b + delim.length(), b = s.find(delim, a))
{
*out++ = std::move(s.substr(a, b - a));
}
*out++ = std::move(s.substr(a, s.length() - a));
return out;
}
Some test cases:
void test()
{
std::vector<std::string> out;
size_t counter;
std::cout << "Empty input:" << std::endl;
out.clear();
splitStrings("", ",", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, empty delimiter:" << std::endl;
out.clear();
splitStrings("Hello, world!", "", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", no delimiter in string:" << std::endl;
out.clear();
splitStrings("abxycdxyxydefxya", "xyz", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", delimiter exists string:" << std::endl;
out.clear();
splitStrings("abxycdxy!!xydefxya", "xy", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", delimiter exists string"
", input contains blank token:" << std::endl;
out.clear();
splitStrings("abxycdxyxydefxya", "xy", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", delimiter exists string"
", nothing after last delimiter:" << std::endl;
out.clear();
splitStrings("abxycdxyxydefxy", "xy", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", only delimiter exists string:" << std::endl;
out.clear();
splitStrings("xy", "xy", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
}
Expected output:
Empty input:
0:
Non-empty input, empty delimiter:
0: Hello, world!
Non-empty input, non-empty delimiter, no delimiter in string:
0: abxycdxyxydefxya
Non-empty input, non-empty delimiter, delimiter exists string:
0: ab
1: cd
2: !!
3: def
4: a
Non-empty input, non-empty delimiter, delimiter exists string, input contains blank token:
0: ab
1: cd
2:
3: def
4: a
Non-empty input, non-empty delimiter, delimiter exists string, nothing after last delimiter:
0: ab
1: cd
2:
3: def
4:
Non-empty input, non-empty delimiter, only delimiter exists string:
0:
1:
One possible way of doing this is finding all occurrences of the split string and storing locations to a list. Then count input string characters and when you get to a position where there is a 'search hit' in the position list then you jump forward by 'length of the split string'. This approach takes a split string of any length. Here is my tested and working solution.
#include <iostream>
#include <string>
#include <list>
#include <vector>
using namespace std;
vector<string> Split(string input_string, string search_string)
{
list<int> search_hit_list;
vector<string> word_list;
size_t search_position, search_start = 0;
// Find start positions of every substring occurence and store positions to a hit list.
while ( (search_position = input_string.find(search_string, search_start) ) != string::npos) {
search_hit_list.push_back(search_position);
search_start = search_position + search_string.size();
}
// Iterate through hit list and reconstruct substring start and length positions
int character_counter = 0;
int start, length;
for (auto hit_position : search_hit_list) {
// Skip over substrings we are splitting with. This also skips over repeating substrings.
if (character_counter == hit_position) {
character_counter = character_counter + search_string.size();
continue;
}
start = character_counter;
character_counter = hit_position;
length = character_counter - start;
word_list.push_back(input_string.substr(start, length));
character_counter = character_counter + search_string.size();
}
// If the search string is not found in the input string, then return the whole input_string.
if (word_list.size() == 0) {
word_list.push_back(input_string);
return word_list;
}
// The last substring might be still be unprocessed, get it.
if (character_counter < input_string.size()) {
word_list.push_back(input_string.substr(character_counter, input_string.size() - character_counter));
}
return word_list;
}
int main() {
vector<string> word_list;
string search_string = " ";
// search_string = "the";
string text = "thetheThis is some text to test with the split-thethe function.";
word_list = Split(text, search_string);
for (auto item : word_list) {
cout << "'" << item << "'" << endl;
}
cout << endl;
}
I am using the brute force string search algorithm to search through a small sentence, however I want the algorithm to return every time it finds the certain string instead of finding it once and then stopping
//Declare and initialise variables
string pat, text;
text = "This is a test sentence, find test within this string";
cout << text << endl;
//User input for pat
cout << "Please enter the string you want to search for" << endl;
cin >> pat;
//Set the length of the pat and text
int patLength = pat.size();
int textLength = text.size();
//Algorithm
for (int i = 0; i < textLength - patLength; ++i)
{
//Do while loop to run through the whole text
do
{
int j;
for (j = 0; j < patLength; j++)
{
if (text[i + j] != pat[j])
break; // Doesn't match here.
}
if (j == patLength)
{
finds.push(i); // Matched here.
}
} while (i < textLength);
}
//Print output
cout << "String: " << pat << " was found at positions: " << finds.top();
The program stores each find in a queue. When I run this program, it asks for the 'pat', then does nothing. I have done a bit of debugging and found that it is probably the do while loop. However I can't find a fix
You could use the std::string::find function combined with a function that you call for each find.
#include <iostream>
#include <functional>
#include <vector>
#include <sstream>
void Algorithm(
const std::string& text, const std::string& pat,
std::function<void(const std::string&,size_t)> f, std::vector<size_t>& positions)
{
size_t pos=0;
while((pos=text.find(pat, pos)) != std::string::npos) {
// store the position
positions.push_back(pos);
// call the supplied function
f(text, pos++);
}
}
// function to call for each position in which the pattern is found
void gotit(const std::string& found_in, size_t pos) {
std::cout << "Found in \"" << found_in << "\" # " << pos << "\n";
}
int main(int argc, char* argv[]) {
std::vector<std::string> args(argv+1, argv+argc);
if(args.size()==0)
args.push_back("This is a test sentence, find test within this string");
for(const auto& text : args) {
std::vector<size_t> found_at;
std::cout << "Please enter the string you want to search for: ";
std::string pat;
std::cin >> pat;
Algorithm(text, pat, gotit, found_at);
std::cout << "collected positions:\n";
for(size_t pos : found_at) {
std::cout << pos << "\n";
}
}
}
My first bit of advice would be to structure your code into separate functions.
Let's say you have a function that returns the position of the pattern's first occurrence in a sequence of characters:
using position = typename std::string::const_iterator;
position first_occurrence(position text_begin, position text_end, const std::string& pattern);
If there is no more occurrence of the pattern, it returns text_end.
You can now write a very simple loop:
auto occurrence = first_occurrence(text_begin, pattern);
while (occurrence != text_end) {
occurrences.push_back(occurrence);
occurrence = first_occurence(occurrence + 1, text_end, pattern);
}
to accumulate all the occurrences of the pattern.
The first_occurrence function already exists in the standard library under the name of std::search. Since C++17, you can customize this function with pattern-searching specialized searchers, such as std::boyer_moore_searcher: it pre-processes the pattern to make it faster to look for in the string. Here's an example application to your problem:
#include <algorithm>
#include <string>
#include <vector>
#include <functional>
using occurrence = typename std::string::const_iterator;
std::vector<occurrence> find_occurrences(const std::string& input, const std::string& pattern) {
auto engine = std::boyer_moore_searcher(pattern.begin(), pattern.end());
std::vector<occurrence> occurrences;
auto it = std::search(input.begin(), input.end(), engine);
while (it != input.end()) {
occurrences.push_back(it);
it = std::search(std::next(it), input.end(), engine);
}
return occurrences;
}
#include <iostream>
int main() {
std::string text = "This is a test sentence, find test within this string";
std::string pattern = "st";
auto occs = find_occurrences(text, pattern);
for (auto occ: occs) std::cout << std::string(occ, std::next(occ, pattern.size())) << std::endl;
}
What would be easiest method to split a string using c++11?
I've seen the method used by this post, but I feel that there ought to be a less verbose way of doing it using the new standard.
Edit: I would like to have a vector<string> as a result and be able to delimitate on a single character.
std::regex_token_iterator performs generic tokenization based on a regex. It may or may not be overkill for doing simple splitting on a single character, but it works and is not too verbose:
std::vector<std::string> split(const string& input, const string& regex) {
// passing -1 as the submatch index parameter performs splitting
std::regex re(regex);
std::sregex_token_iterator
first{input.begin(), input.end(), re, -1},
last;
return {first, last};
}
Here is a (maybe less verbose) way to split string (based on the post you mentioned).
#include <string>
#include <sstream>
#include <vector>
std::vector<std::string> split(const std::string &s, char delim) {
std::stringstream ss(s);
std::string item;
std::vector<std::string> elems;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
// elems.push_back(std::move(item)); // if C++11 (based on comment from #mchiasson)
}
return elems;
}
Here's an example of splitting a string and populating a vector with the extracted elements using boost.
#include <boost/algorithm/string.hpp>
std::string my_input("A,B,EE");
std::vector<std::string> results;
boost::algorithm::split(results, my_input, boost::is_any_of(","));
assert(results[0] == "A");
assert(results[1] == "B");
assert(results[2] == "EE");
Another regex solution inspired by other answers but hopefully shorter and easier to read:
std::string s{"String to split here, and here, and here,..."};
std::regex regex{R"([\s,]+)"}; // split on space and comma
std::sregex_token_iterator it{s.begin(), s.end(), regex, -1};
std::vector<std::string> words{it, {}};
I don't know if this is less verbose, but it might be easier to grok for those more seasoned in dynamic languages such as javascript. The only C++11 features it uses is auto and range-based for loop.
#include <string>
#include <cctype>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
string s = "hello how are you won't you tell me your name";
vector<string> tokens;
string token;
for (const auto& c: s) {
if (!isspace(c))
token += c;
else {
if (token.length()) tokens.push_back(token);
token.clear();
}
}
if (token.length()) tokens.push_back(token);
return 0;
}
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
vector<string> split(const string& str, int delimiter(int) = ::isspace){
vector<string> result;
auto e=str.end();
auto i=str.begin();
while(i!=e){
i=find_if_not(i,e, delimiter);
if(i==e) break;
auto j=find_if(i,e, delimiter);
result.push_back(string(i,j));
i=j;
}
return result;
}
int main(){
string line;
getline(cin,line);
vector<string> result = split(line);
for(auto s: result){
cout<<s<<endl;
}
}
My choice is boost::tokenizer but I didn't have any heavy tasks and test with huge data.
Example from boost doc with lambda modification:
#include <iostream>
#include <boost/tokenizer.hpp>
#include <string>
#include <vector>
int main()
{
using namespace std;
using namespace boost;
string s = "This is, a test";
vector<string> v;
tokenizer<> tok(s);
for_each (tok.begin(), tok.end(), [&v](const string & s) { v.push_back(s); } );
// result 4 items: 1)This 2)is 3)a 4)test
return 0;
}
This is my answer. Verbose, readable and efficient.
std::vector<std::string> tokenize(const std::string& s, char c) {
auto end = s.cend();
auto start = end;
std::vector<std::string> v;
for( auto it = s.cbegin(); it != end; ++it ) {
if( *it != c ) {
if( start == end )
start = it;
continue;
}
if( start != end ) {
v.emplace_back(start, it);
start = end;
}
}
if( start != end )
v.emplace_back(start, end);
return v;
}
#include <string>
#include <vector>
#include <sstream>
inline vector<string> split(const string& s) {
vector<string> result;
istringstream iss(s);
for (string w; iss >> w; )
result.push_back(w);
return result;
}
Here is a C++11 solution that uses only std::string::find(). The delimiter can be any number of characters long. Parsed tokens are output via an output iterator, which is typically a std::back_inserter in my code.
I have not tested this with UTF-8, but I expect it should work as long as the input and delimiter are both valid UTF-8 strings.
#include <string>
template<class Iter>
Iter splitStrings(const std::string &s, const std::string &delim, Iter out)
{
if (delim.empty()) {
*out++ = s;
return out;
}
size_t a = 0, b = s.find(delim);
for ( ; b != std::string::npos;
a = b + delim.length(), b = s.find(delim, a))
{
*out++ = std::move(s.substr(a, b - a));
}
*out++ = std::move(s.substr(a, s.length() - a));
return out;
}
Some test cases:
void test()
{
std::vector<std::string> out;
size_t counter;
std::cout << "Empty input:" << std::endl;
out.clear();
splitStrings("", ",", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, empty delimiter:" << std::endl;
out.clear();
splitStrings("Hello, world!", "", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", no delimiter in string:" << std::endl;
out.clear();
splitStrings("abxycdxyxydefxya", "xyz", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", delimiter exists string:" << std::endl;
out.clear();
splitStrings("abxycdxy!!xydefxya", "xy", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", delimiter exists string"
", input contains blank token:" << std::endl;
out.clear();
splitStrings("abxycdxyxydefxya", "xy", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", delimiter exists string"
", nothing after last delimiter:" << std::endl;
out.clear();
splitStrings("abxycdxyxydefxy", "xy", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
std::cout << "Non-empty input, non-empty delimiter"
", only delimiter exists string:" << std::endl;
out.clear();
splitStrings("xy", "xy", std::back_inserter(out));
counter = 0;
for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
std::cout << counter << ": " << *i << std::endl;
}
}
Expected output:
Empty input:
0:
Non-empty input, empty delimiter:
0: Hello, world!
Non-empty input, non-empty delimiter, no delimiter in string:
0: abxycdxyxydefxya
Non-empty input, non-empty delimiter, delimiter exists string:
0: ab
1: cd
2: !!
3: def
4: a
Non-empty input, non-empty delimiter, delimiter exists string, input contains blank token:
0: ab
1: cd
2:
3: def
4: a
Non-empty input, non-empty delimiter, delimiter exists string, nothing after last delimiter:
0: ab
1: cd
2:
3: def
4:
Non-empty input, non-empty delimiter, only delimiter exists string:
0:
1:
One possible way of doing this is finding all occurrences of the split string and storing locations to a list. Then count input string characters and when you get to a position where there is a 'search hit' in the position list then you jump forward by 'length of the split string'. This approach takes a split string of any length. Here is my tested and working solution.
#include <iostream>
#include <string>
#include <list>
#include <vector>
using namespace std;
vector<string> Split(string input_string, string search_string)
{
list<int> search_hit_list;
vector<string> word_list;
size_t search_position, search_start = 0;
// Find start positions of every substring occurence and store positions to a hit list.
while ( (search_position = input_string.find(search_string, search_start) ) != string::npos) {
search_hit_list.push_back(search_position);
search_start = search_position + search_string.size();
}
// Iterate through hit list and reconstruct substring start and length positions
int character_counter = 0;
int start, length;
for (auto hit_position : search_hit_list) {
// Skip over substrings we are splitting with. This also skips over repeating substrings.
if (character_counter == hit_position) {
character_counter = character_counter + search_string.size();
continue;
}
start = character_counter;
character_counter = hit_position;
length = character_counter - start;
word_list.push_back(input_string.substr(start, length));
character_counter = character_counter + search_string.size();
}
// If the search string is not found in the input string, then return the whole input_string.
if (word_list.size() == 0) {
word_list.push_back(input_string);
return word_list;
}
// The last substring might be still be unprocessed, get it.
if (character_counter < input_string.size()) {
word_list.push_back(input_string.substr(character_counter, input_string.size() - character_counter));
}
return word_list;
}
int main() {
vector<string> word_list;
string search_string = " ";
// search_string = "the";
string text = "thetheThis is some text to test with the split-thethe function.";
word_list = Split(text, search_string);
for (auto item : word_list) {
cout << "'" << item << "'" << endl;
}
cout << endl;
}
I'm trying not to use any storage containers. I don't know if it's even possible. Here is what I have so far. (I'm getting a segmentation fault).
#include <iostream>
#include <string>
using namespace std;
void foo(string s)
{
size_t pos;
pos = s.find(' ');
if(pos == string::npos)
return;
foo(s.erase(0, pos));
cout << s.substr(0, pos) << " ";
}
int main()
{
foo("hello world");
return 0;
}
I know there's probably many things wrong with this code. So rip away. I'm eager to learn. I'm trying to imitate a post order print as you would do in a reverse print of a singly linked list. Thanks.
EDIT:
An example:
"You are amazing" becomes "amazing are You"
The segfault is a stack overflow.
foo( "hello world" ) erases everything up to the first space (" world") and recurses.
foo( " world" ) erases everything up to the first space (" world") and recurses.
foo( " world" )... you get the idea.
Also, once you called foo( s.erase( 0, pos ) ), trying to print s.substr( 0, pos ) after the recursion returns does not make sense. You need to save the substring somewhere before you erase it, so you still have it to print afterwards.
void foo(string s)
{
size_t pos = s.find(' '); // declare-and-use in one line
string out = s.substr( 0, pos ); // saving the substring
if ( pos != string::npos )
{
foo( s.erase( 0, pos + 1 ) ); // recurse, skipping the space...
cout << " "; // ...but *print* the space
}
cout << out; // print the saved substring
}
The problem is that your recursion continues until you run out of memory.
Pay attention to this line:
if(pos == string::npos)
when your erase the substring you don't erase the white space so in the next recursion s.find returns pos = 0 which means that your recursion never ends.
Here is a code that works. Also note that I added a level variable to be able to control the behaviour on the first level (in this case add a endl)
#include <iostream>
#include <string>
using namespace std;
void foo(string s, int l)
{
size_t pos;
pos = s.find(' ');
if(pos == string::npos){
cout << s << " ";
return;
}
string temp = s.substr(0, pos);
foo(s.erase(0, pos+1),l+1);
cout << temp << " ";
if(l == 0)
cout << endl;
}
int main()
{
foo("hello world", 0);
return 0;
}
An approach to recursion, which may allow your compiler to transform automatically to iteration, is to accumulate the result in the function arguments. This will be familiar if you've written recursive functions in any of the Lisp family of languages:
#include <iostream>
#include <string>
std::string reverse_words(const std::string& s, const std::string& o = {})
{
using std::string;
const auto npos = string::npos;
static const string whitespace(" \n\r\t");
// find start and end of the first whitespace block
auto start = s.find_first_of(whitespace);
if (start == npos)
return s + o;
auto end = s.find_first_not_of(whitespace, start);
if (end == npos)
return s + o;
auto word = s.substr(0, start);
auto space = s.substr(start, end-start);
auto rest = s.substr(end);
return reverse_words(rest, space + word + o);
}
int main()
{
std::cout << reverse_words("hello to all the world") << std::endl;
std::cout << reverse_words(" a more difficult\n testcase ") << std::endl;
return 0;
}
I tried to make a brief example by using standard algorithms. I also handles more kinds of spaces than just standard whitespace (tabs for instance).
#include <cctype>
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
void print_reverse(string words) {
// Termination condition
if(words.empty())
return;
auto predicate = (int(*)(int))isspace;
auto sit = begin(words);
auto wit = find_if_not(sit, end(words), predicate);
auto nit = find_if (wit, end(words), predicate);
print_reverse(string(nit, end(words)));
// word spaces
cout << string(wit, nit) << string(sit, wit);
}
int main() {
string line;
getline(cin, line);
print_reverse(line);
cout << endl;
}
Here is an example run:
$ ./print-out-the-words-of-a-line-in-reverse-order-through-recursion
You are amazing
amazing are You
The key is in adding 1 to pos in the erase statement.
So try:
#include <iostream>
#include <string>
using namespace std;
void foo(string s)
{
size_t pos;
pos = s.find(' ');
if(pos == string::npos)
{
cout << s << " ";
return;
}
string out = s.substr(0, pos);
foo(s.erase(0, pos+1));
cout << out << " ";
}
int main()
{
foo("hello world");
cout << endl;
return 0;
}
EDIT
Alternatively you could use a char* instead of a std::string, then you do not need to make a temp variable. Try it online.
#include <iostream>
#include <cstring>
void foo(char* s)
{
char* next = std::strchr(s, ' ');
if(next != nullptr)
{
foo(next + 1);
*next = 0;
}
std::cout << s << " ";
}
int main()
{
char s[] = "You are amazing";
foo(s);
std::cout << std::endl;
}
The problem is that you're not doing anything with the last word and you're not doing anything with the remaining chunk.
If you have a recursive reverse printer, you'll want something like this (pseudocode):
def recursive-reverse(string) {
pos = string.find-last(" ");
if pos doesn't exist {
print string;
return;
} else {
print string.create-substring(pos+1, string.end);
recursive-reverse(string.create-substring(0, pos));
}
}
To implement this in C++:
#include <iostream>
#include <string>
void recursive_reverse(std::string &s) {
// find the last space
size_t pos = s.find_last_of(" ");
// base case - there's no space
if(pos == std::string::npos) {
// print only word in the string
std::cout << s << std::endl;
// end of recursion
return;
} else {
// grab everything after the space
std::string substring = s.substr(pos+1);
// print it
std::cout << substring << std::endl;
// grab everything before the space
std::string rest = s.substr(0, pos);
// recursive call on everything before the space
recursive_reverse(rest);
}
}
int main() {
std::string s("Hello World!");
recursive_reverse(s);
return 0;
}
ideone
in below programme, I need to insert string till '|' character in set .
but after erasing charcter '|' in my code , I am not able to skip all the character till the end.
input: +919845012345, 0987654321, 987654320|9845012345, +91987654321, 0987654320, 987654323, 987654320
expected output: +919845012345, 0987654321, 987654320
Note: (1) after entering the data , type exit .
(2) substring function can't be used for this purpose.
#include<iostream>
#include <unordered_set>
#include<string>
using namespace std;
int main()
{
string str;
string::iterator it;
unordered_set <string> s;
while(getline(cin,str)) //
{
if(str=="exit")
{
break;
}
for (it= str.begin(); it !=str.end(); it++)
{
if (*it =='|')
{
it = str.erase(it);
}
}
s.insert(str);
}
for ( unordered_set<string> ::const_iterator itr = s.begin(); itr != s.end(); ++itr) {
cout<<*itr<<endl;
}
return 1;
}
Try using string::find_first_of
see here:
*http://www.cplusplus.com/reference/string/string/find_first_of/*
Use the above function to find the index of the next delimiter "|" and create a string from the range (substring).
This method is also very fast compared to the alternatives.
Another option is to use C's strtok on a copy of the string.
I think there's nothing too difficult..
#include <iostream>
using namespace std;
int main()
{
string myString = "+919845012345, 0987654321, 987654320|9845012345, +91987654321, 0987654320, 987654323, 987654320";
for (unsigned int i = 0; (myString[i] != '|') && (i < myString.length()); i++)
cout << myString[i];
cout << endl;
return 0;
}
Instead of the code
for (it= str.begin(); it !=str.end(); it++)
{
if (*it =='|')
{
it = str.erase(it);
}
}
try to use
str.erase( str.find( '|' ) );
Demonstrative example
std::string s = "+919845012345, 0987654321, 987654320|9845012345, +91987654321, 0987654320, 987654323, 987654320";
std::cout << s << std::endl;
s.erase( s.find( '|' ) );
std::cout << s << std::endl;
Output is
+919845012345, 0987654321, 987654320|9845012345, +91987654321, 0987654320, 98765
4323, 987654320
+919845012345, 0987654321, 987654320