I am trying to find an optimal way to find a pattern of a string and compare. For example, I have s1 = "red blue blue red red yellow", and s2 = "abbaac". This would match because they have the same pattern.
My thinking of doing this would be iterate through s1 and s2, use a vector container to record the corresponding place's count (for s1 would be corresponding word's count, and for s2 would be corresponding letter's count) and then compare.
This is really inefficient because I iterator through the whole s1 and s2. If s1 = "red blue red red red yellow" and s2 = "abbaac". After the third red, there is essentially no point to keep iterating it through.
So, any better idea on how to do this?
Code:
#include "stdafx.h"
#include <iostream>
#include <string>
#include <array>
#include <sstream>
#include <vector>
#include <algorithm>
using namespace std;
vector<int> findPattern(string pattern){
vector<int> counts;
for (int i = 0; i < pattern.size(); ++i){
counts.push_back(0);
int counter = 0;
for (int j = i + 1; j < pattern.size(); ++j){
if (pattern[i] == pattern[j]){
++counter;
}
counts[i] = counter;
}
}
return counts;
}
vector<int> findPatternLong(string pattern){
istringstream iss (pattern);
string word;
vector<string> v;
while (iss >> word){
v.push_back(word);
}
vector<int> counts2;
for (int i = 0; i < v.size(); ++i){
counts2.push_back(0);
int counter = 0;
for (int j = i + 1; j < v.size(); ++j){
if (v[i] == v[j]){
++counter;
}
counts2[i] = counter;
}
}
return counts2;
}
int main(int argc, char * argv[]){
vector<int> v1 = findPattern("abbaac");
vector<int> v2 = findPatternLong("red blue blue red red yellow");
if (v1.size() == v2.size()){
for (int i = 0; i < v1.size(); ++i){
if (v1[i] != v2[i]){
cout << "Unmatch" << endl;
return false;
}
}
cout << "match" << endl;
return true;
} else
cout << "Unmatch" << endl;
return 0;
}
#Tony beat me with same idea, but since I already typed this, here it goes :-)
First of all, don't worry so much about efficiency and focus on correctness: indeed, premature optimization is the root of all evil. Write test cases and make sure your code passes each one.
Second, I think I would start with a maps/dictionary D, and have a loop in which I'd parse one element of each string (a word in s1, let's call it "w" and a character in your s2, say "c"), choose one element as the key (say the "c" characters) and check if "c" already has an entry in the dictionary:
If we ran out of elements at the same time, the strings match
If we ran out of elements on one side, we know there's no match
If "c" doesn't have an entry in D, store the current values: D[c] = w;
else if "c" already has an entry, check if the entry matches the value found on the string: is D[c] == w? If it doesn't we know there's no match
If that code works, then optimization could start. In your example, maybe we could use a simple array instead of a dictionary because ASCII characters are a small finite set.
It's not the most efficient code, but close to simplest:
std::map<char, std::string> letter_to_word;
std::set<std::string> words_seen;
std::istringstream iss(s1);
std::string word;
for (std::string::size_t i = 0; i < s2.size(); ++i)
{
if (!(iss >> word))
return false; // more letters than words
std::string& expected_word = letter_to_word[s2[i]];
if (expected_word == "")
{
// if different letters require different words...
if (words_seen.find(word) != words_seen.end())
return false; // multiple letters for same word
words_seen.insert(word);
expected_word = word; // first time we've seen letter, remember associated word
}
else if (expected_word != word)
return false; // different word for same letter
}
return !(iss >> word); // check no surplus words
You don't need two vectors.
When processing the second string, compare the count of the first pattern, to the first entry. If it matches, keep going otherwise stop. Repeat for the rest of the patterns in the second string.
You don't need to store the pattern counts of the second string.
EDIT
I just read that the question had the patterns in a string and this answer pertains to comparing collections of varying types. I suppose the answer still holds a little water if the 2 input strings were first converted :)
I would not say this is the most efficient solution, but I like how it is extensible.
Firstly, there is the PatternResult class. It stores the result of a pattern:
class PatternResult {
private:
std::vector<int> result_;
public:
PatternResult(const std::vector<int>& result) : result_(result) {
};
bool operator == (const PatternResult& rhs) const {
if(result_.size() != rhs.result_.size())
return false;
else {
for(std::vector<int>::size_type r(0);
r < result_.size();
++r) {
if(result_[r] != rhs.result_[r])
return false;
};
return true;
};
};
}; // eo class PatternResult
It takes a vector of integers, the value of which denotes it's value. We overload == to compare two pattern results, meaning they have the same sequence irrespective of the source data.
Then we need a pattern counter that can assign the same sequence numbers, but take any type:
template<class T>
class PatternCounter {
private:
typedef std::vector<T> vec_type;
typedef std::map<T, int> map_type;
map_type found_;
int counter_;
public:
PatternCounter() : counter_(1) {
};
PatternResult count(const vec_type& input ){
std::vector<int> ret;
for(vec_type::const_iterator cit(input.begin());
cit != input.end();
++cit) {
if(found_.find(*cit) != found_.end()) {
ret.push_back(found_[*cit]);
} else {
found_[*cit] = counter_;
ret.push_back(counter_);
++counter_;
};
};
return PatternResult(ret);
};
};
And we're done. Test code:
std::vector<std::string> inp1;
inp1.push_back("red");
inp1.push_back("blue");
inp1.push_back("blue");
inp1.push_back("red");
inp1.push_back("yellow");
std::vector<char> inp2;
inp2.push_back('a');
inp2.push_back('b');
inp2.push_back('b');
inp2.push_back('a');
inp2.push_back('c');
PatternCounter<std::string> counter1;
PatternCounter<char> counter2;
PatternResult res1(counter1.count(inp1));
PatternResult res2(counter2.count(inp2));
if(res1 == res2) {
// pattern sequences are equal
};
Note this was quick and dirty, I am sure it could be made more efficient.
Basically, you want to check that the sequence follows the same order. You're not worried about what the sequence actually is: first second first first third is good enough. Now, you could do this with a container that maps a string to an int in some way. However, you would be storing copies of each string and you're ignoring the fact that you don't really care about string values. For tiny test cases, this wouldn't matter, but for a large sequence of long words, you're quickly chewing up memory when you don't need to.
So let's use the fact that we don't care about the string values or about storing them. If that's the case, we can use a hash function to transform our strings to simple size_t values with a fairly strong guarantee that they're going to be unique. However, the hashes are not sequential and we will need to retrieve the sequence based on the hash value. The simplest way to record their sequence is to map them to the size of the map for easy lookup. The last piece of the puzzle is to check that the hashes are in the same sequence.
I'm also assuming that you don't just want to compare a sentence with a word, but maybe 2 words or two sentences. Here's a quick C++11 sample that basically does the above and doesn't hold anything in memory unless it needs to.
Ofcourse, this can still be optimized more - for example, executing things parallel.
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <sstream>
/*
s1 = "red blue blue red red yellow"
s2 = "abbaac"
This would match because they have the same pattern.
*/
typedef std::map<size_t,size_t> hash_map;
typedef std::vector<std::string> wordlist;
size_t ordered_symbol( hash_map &h, std::string const& word )
{
std::hash<std::string> hash_fn;
size_t hash = hash_fn(word);
if(h.find(hash)==h.end())
{
size_t const sequence = h.size();
h[hash] = sequence;
return sequence;
}
return h[hash];
}
wordlist create_wordlist( std::string const& str )
{
if(str.find_first_of(' ') != std::string::npos)
{
wordlist w1;
std::stringstream sstr(str);
std::string s;
while(sstr>>s)
w1.push_back(s);
return w1;
}
wordlist w2;
for(auto i : str)
{
std::string s;
s.append(1,i);
w2.push_back(s);
}
return w2;
}
bool pattern_matches( std::string const& s1, std::string const& s2 )
{
wordlist const w1 = create_wordlist(s1);
wordlist const w2 = create_wordlist(s2);
if(w1.size()!=w2.size())
return false;
hash_map h1,h2;
for( size_t i = 0; i!=w1.size(); ++i)
if(ordered_symbol(h1,w1[i])!=ordered_symbol(h2,w2[i]))
return false;
return true;
}
void test( std::string const& s1, std::string const& s2 )
{
std::cout<<"["<<s1<<"] "
<<(pattern_matches(s1,s2)? "<==>" : "<=!=>")
<<"["<<s2<<"]\n";
}
int main()
{
test("red blue blue red red yellow","abbaac");
test("red blue blue red red yellow","first second second first first third");
test("abbaac","12211g");
test("abbaac","red blue blue red red yellow");
test("abbgac","red blue blue red red yellow");
return 0;
}
//Output:
//[red blue blue red red yellow] <==>[abbaac]
//[red blue blue red red yellow] <==>[first second second first first third]
//[abbaac] <==>[12211g]
//[abbaac] <==>[red blue blue red red yellow]
//[abbgac] <=!=>[red blue blue red red yellow]
EDIT: Here's a non C++11 version that should work on VS2010. However, since C++03 does not include a string hash function in the standard library, this example uses a hash function taken from stack overflow. A much better hash function to use would be this one if you have access to the boost libraries.
Related
I was given a project in class and almost have it finished, I am required to take a string of numbers and letters and return that string with the numbers printed first followed by the letters in reverse order (ex. abc123 should return 123cba). As of now my code returns a string with the numbers first and the original order of the letters (ex. abc123 returns 123abc). I would be able to do this with two loops however the assignment asks that my code only iterates though the initial string one time. Here is the code I have so far...
#include <iostream>
#include <string>
#include "QueType.h"
#include "StackType.h"
using namespace std;
int main ()
{
QueType<char> myQueue;
StackType<char> myStack;
string myString="hello there123";
char curchar;
string numbers, letters;
for (int i = 0; i < myString.length(); i++) {
if (isdigit(myString.at(i))) {
myQueue.Enqueue(myString.at(i));
myQueue.Dequeue(curchar);
numbers += curchar;
//cout<<numbers<<endl;
}
else if (islower(myString.at(i))) {
myStack.Push(myString.at(i));
curchar = myStack.Peek();
myStack.Pop();
letters += curchar;
//cout<<curchar<<endl;
}
}
cout<<(myString = numbers + letters)<<endl;
}
In my code, I have two .h files that set up a stack and a queue. With the given string, the code loops through the string looking to see if it sees a letter or number. With a number the spot in the string is then saved to a queue, and with a letter it is saved to the stack.
The only other way i can think of reversing the order of the letters is in the if else statement instead of having char = myStack.Peek() every loop, change it to char += myStack.Peek() however I get weird lettering when that happens.
since you already got the string with letters you can basically reverse it and that's it.
//emplace version:
void reverse_str(std::string& in)
{
std::reverse(in.begin(), in.end());
}
//copy version
std::string reverse_str(std::string in)
{
std::reverse(in.begin(), in.end());
return in;
}
in your case the emplace version would be the best match.
in other cases (e.g. when you want to preserve the original string) the copy version is preferred.
adding an example to make it as clean as possible.
int main()
{
std::string inputstr = "123abc";
std::string numbers{};
std::string letters{};
for(auto c : inputstr)
{
if(isdigit(c))
numbers += c;
else
letters += c;
}
reverse_str(letters); //using the emplace version
std::cout << numbers + letters;
}
Here's my take. It only loops through the string once. I don't have your types, so I'm just using the std versions.
std::string output;
output.reserve( myString.size() );
std::stack<char> stack;
for ( char c : myString ) {
if ( std::isdigit( c ) ) // if it's a number, just add it to the output
output.push_back( c );
else // otherwise, add the character to the stack
stack.push( c );
}
// string is done being processed, so use the stack to get the
// other characters in reverse order
while ( !stack.empty() ) {
output.push_back( stack.top() );
stack.pop();
}
std::cout << output;
working example: https://godbolt.org/z/eMazcGsMf
Note: wasn't sure from your description how to handle characters other than letters and numbers, so treated them the same as letters.
One way to do this is as follows:
Version 1
#include <iostream>
#include <string>
int main() {
std::string s = "abc123";
std::string output;
output.resize(s.size());
int i = output.length() - 1;
int j = 0;
for(char &c: s)
{
if(!std::isdigit(c))
{
output.at(i) = c;
--i;
}
else
{
output.at(j) = c;
++j;
}
}
std::cout<<output<<std::endl;
}
You can also use iterators in the above program to obtain the desired result as shown in version 2.
Version 2
#include <iostream>
#include <string>
int main() {
std::string s = "abfsc13423";
std::string output;
output.resize(s.size());
std::string::reverse_iterator iter = output.rbegin();
std::string::iterator begin = output.begin();
for(char &c: s)
{
if(!std::isdigit(c))
{
*iter = c;
++iter;
}
else
{
*begin = c;
++begin;
}
}
std::cout<<output<<std::endl;
}
Background:
I got asked this question today in a online practice interview and I had a hard time figuring out a custom comparator to sort. Here is the question
Question:
Implement a document scanning function wordCountEngine, which receives a string document and returns a list of all unique words in it and their number of occurrences, sorted by the number of occurrences in a descending order. If two or more words have the same count, they should be sorted according to their order in the original sentence. Assume that all letters are in english alphabet. You function should be case-insensitive, so for instance, the words “Perfect” and “perfect” should be considered the same word.
The engine should strip out punctuation (even in the middle of a word) and use whitespaces to separate words.
Analyze the time and space complexities of your solution. Try to optimize for time while keeping a polynomial space complexity.
Examples:
input: document = "Practice makes perfect. you'll only
get Perfect by practice. just practice!"
output: [ ["practice", "3"], ["perfect", "2"],
["makes", "1"], ["youll", "1"], ["only", "1"],
["get", "1"], ["by", "1"], ["just", "1"] ]
My idea:
The first think I wanted to do was first get the string without punctuation and all in lower case into a vector of strings. Then I used an unordered_map container to store the string and a count of its occurrence. Where I got stuck was creating a custom comparator to make sure that if I have a string that has the same count then I would sort it based on its precedence in the actual given string.
Code:
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <sstream>
#include <iterator>
#include <numeric>
#include <algorithm>
using namespace std;
struct cmp
{
bool operator()(std::string& word1, std::string& word2)
{
}
};
vector<vector<string>> wordCountEngine( const string& document )
{
// your code goes here
// Step 1
auto doc = document;
std::string str;
remove_copy_if(doc.begin(), doc.end(), std::back_inserter(str),
std::ptr_fun<int, int>(&std::ispunct));
for(int i = 0; i < str.size(); ++i)
str[i] = tolower(str[i]);
std::stringstream ss(str);
istream_iterator<std::string> begin(ss);
istream_iterator<std::string> end;
std::vector<std::string> vec(begin, end);
// Step 2
std::unordered_map<std::string, int> m;
for(auto word : vec)
m[word]++;
// Step 3
std::vector<std::vector<std::string>> result;
for(auto it : m)
{
result.push_back({it.first, std::to_string(it.second)});
}
return result;
}
int main() {
std::string document = "Practice makes perfect. you'll only get Perfect by practice. just practice!";
auto result = wordCountEngine(document);
for(int i = 0; i < result.size(); ++i)
{
for(int j = 0; j < result[0].size(); ++j)
{
std::cout << result[i][j] << " ";
}
std::cout << "\n";
}
return 0;
}
If anyone can help me with learning how to build a custom comparator for this code I would really appreciate it.
You could use a std::vector<std::pair<std::string, int>>, with each pair representing one word and the number of occurrences of that word in the sequence. Using a vector will help to maintain the order of the original sequence when two or more words have the same count. Finally sort by occurrences.
#include <vector>
#include <algorithm>
#include <string>
#include <sstream>
std::vector<std::vector<std::string>> wordCountEngine(const std::string& document)
{
std::vector<std::pair<std::string, int>> words;
std::istringstream ss(document);
std::string word;
//Loop through words in sequence
while (getline(ss, word, ' '))
{
//Convert to lowercase
std::transform(word.begin(), word.end(), word.begin(), tolower);
//Remove punctuation characters
auto it = std::remove_if(word.begin(), word.end(), [](char c) { return !isalpha(c); });
word.erase(it, word.end());
//Find this word in the result vector
auto pos = std::find_if(words.begin(), words.end(),
[&word](const std::pair<std::string, int>& p) { return p.first == word; });
if (pos == words.end()) {
words.push_back({ word, 1 }); //Doesn't occur -> add it
}
else {
pos->second++; //Increment count
}
}
//Sort vector by word occurrences
std::sort(words.begin(), words.end(),
[](const std::pair<std::string, int>& p1, const std::pair<std::string, int>& p2) { return p1.second > p2.second; });
//Convert to vector<vector<string>>
std::vector<std::vector<std::string>> result;
result.reserve(words.size());
for (auto& p : words)
{
std::vector<std::string> v = { p.first, std::to_string(p.second) };
result.push_back(v);
}
return result;
}
int main()
{
std::string document = "Practice makes perfect. you'll only get Perfect by practice. just practice!";
auto result = wordCountEngine(document);
for (auto& word : result)
{
std::cout << word[0] << ", " << word[1] << std::endl;
}
return 0;
}
Output:
practice, 3
perfect, 2
makes, 1
youll, 1
only, 1
get, 1
by, 1
just, 1
In step2, try this:
std::vector<std::pair<std::pair<std::string, int>, int>> m;
Here, the pair stores the string and this index of its occurance, and the vector stores the pair and the count of its occurances. Write a logic, to sort according to the count first and then if the counts are same, then sort it according to the position of its occurance.
bool sort_vector(const std::pair<const std::pair<std::string,int>,int> &a, const std::pair<const std::pair<std::string,int>,int> &b)
{
if(a.second==b.second)
{
return a.first.second<b.first.second
// This will make sure that if the no of occurances of each string is same, then it will be sorted according to the position of the string
}
return a.second>b.second
//This will make sure that the strings are sorted in the order to return the string having higher no of occurances first.
}
You have to write a logic to count the number of occurrences and the index of occurrence of each word in the string.
How to check if two strings have any common part (sub-string) in c++.
I thought of sorting the strings and then using loops on both the strings to check if they have a common part. But I am not sure. And I also thought that this would not be the optimal strategy for the question
Consider strings - 'batman' and 'catman'
They have a common part 'atman'.
P.S. I am not checking for more than one characters. eg - 'apple' and 'batman' have a in common but I am interested in sub-string (min. two common consecutive characters).
This might not be the most efficient code, but so you can get an idea of how it would work in the batman and catman case:
Note: This does solve other cases like "batmanuc" and "catmanic", which would be "atman".
It is not perfect nor the most efficient, but it can help you understand a way to do it and how to manage positions of arrays. From here, implementing it in your own way and adding details is up to you!
like if max>2then print the array, if not, dont print it, for example.
#include <iostream>
using namespace std;
int main()
{
char arr1[10]="batmanuc";
char arr2[10]="catmanic";
char common[10];
int cont=0;
int max=0;
for (int i=0;i<10;i++){
if(arr1[i]==arr2[i]){
if(cont==max){
common[cont]=arr1[i];
cont++;
max++;
}
}
else cont=0;
}
printf("%s",common);
return 0;
}
A stupid algorithm - for each window size from a maximum window (equal to the max of string lengths) to minimum window size (equal to 2, stated in the question), for each position in both strings compare each position with each position in both string with each window_size.
#include <iostream>
#include <string>
#include <cstring>
#include <cassert>
#include <cstdio>
std::string find_common_part(std::string one, std::string two) {
const auto onesize = one.size();
const auto twosize = two.size();
const auto onebegin = one.begin();
const auto twobegin = two.begin();
// min. two common consecutive characters
for (std::size_t window_size = std::max(onesize, twosize);
window_size >= 2;
--window_size) {
for (auto onepos = onebegin,
oneposmax = onebegin + (onesize - window_size);
onepos <= oneposmax;
++onepos) {
for (auto twopos = twobegin,
twoposmax = twobegin + (twosize - window_size);
twopos <= twoposmax;
++twopos) {
if (std::equal(onepos, onepos + window_size, twopos)) {
return std::string(onepos, onepos + window_size);
}
}
}
}
return std::string();
}
int main()
{
std::cout << find_common_part("catman", "batman") << std::endl;
assert(find_common_part("catman", "batman") == "atman");
assert(find_common_part("batman", "manbat") == "man" ||
find_common_part("batman", "manbat") == "bat");
return 0;
}
This function will give you the longest common substring of two strings: (Probably not the fastest way you would do it)
std::string GetLongestCommonSubstring(std::string const& a, std::string const& b) {
std::vector<std::string> substrings;
for (auto beg = a.begin(); beg != std::prev(a.end()); ++beg)
for (auto end = beg; end != a.end(); ++end)
if (b.find(std::string(beg, std::next(end))) != std::string::npos)
substrings.emplace_back(beg, std::next(end));
return *std::max_element(substrings.begin(), substrings.end(),
[](auto& elem1, auto& elem2) { return elem1.length() < elem2.length(); });
}
Example:
int main() {
std::cout << GetLongestCommonSubstring("batman", "catman") << std::endl;
}
Output:
atman
I am faced with a simple yet complex challenge today.
In my program, I wish to insert a - character every three characters of a string. How would this be accomplished? Thank you for your help.
#include <iostream>
int main()
{
std::string s = "thisisateststring";
// Desired output: thi-sis-ate-sts-tri-ng
std::cout << s << std::endl;
return 0;
}
There is no need to "build a new string".
Loop a position iteration, starting at 3, incrementing by 4 with each pass, inserting a - at the position indicated. Stop when the next insertion point would breach the string (which has been growing by one with each pass, thus the need for the 4 slot skip):
#include <iostream>
#include <string>
int main()
{
std::string s = "thisisateststring";
for (std::string::size_type i=3; i<s.size(); i+=4)
s.insert(i, 1, '-');
// Desired output: thi-sis-ate-sts-tri-ng
std::cout << s << std::endl;
return 0;
}
Output
thi-sis-ate-sts-tri-ng
just take an empty string and append "-" at every count divisible by 3
#include <iostream>
int main()
{
std::string s = "thisisateststring";
std::string res="";
int count=0;
for(int i=0;i<s.length();i++){
count++;
res+=s[i];
if(count%3==0){
res+="-";
}
}
std::cout << res << std::endl;
return 0;
}
output
thi-sis-ate-sts-tri-ng
A general (and efficient) approach is to build a new string by iterating character-by-character over the existing one, making any desired changes as you go. In this case, every third character you can insert a hyphen:
std::string result;
result.reserve(s.size() + s.size() / 3);
for (size_t i = 0; i != s.size(); ++i) {
if (i != 0 && i % 3 == 0)
result.push_back('-');
result.push_back(s[i]);
}
Simple. Iterate the string and build a new one
Copy each character from the old string to the new one and every time you've copied 3 characters add an extra '-' to the end of the new string and restart your count of copied characters.
Like 99% problems with text, this one can be solved with a regular expression one-liner:
std::regex_replace(input, std::regex{".{3}"}, "$&-")
However, it brings not one, but two new problems:
it is not a very performant solution
regex library is huge and bloats resulting binary
So think twice.
You could write a simple functor to add the hyphens, like this:
#include <iostream>
struct inserter
{
unsigned n = 0u;
void operator()(char c)
{
std::cout << c;
if (++n%3 == 0) std::cout << '-';
}
};
This can be passed to the standard for_each() algorithm:
#include <algorithm>
int main()
{
const std::string s = "thisisateststring";
std::for_each(s.begin(), s.end(), inserter());
std::cout << std::endl;
}
Exercise: extend this class to work with different intervals, output streams, replacement characters and string types (narrow or wide).
I'm trying to figure out how to can fold a word from a string. For example "code" after the folding would become "ceod". Basically start from the first character and then get the last one, then the second character. I know the first step is to start from a loop, but I have no idea how to get the last character after that. Any help would be great. Heres my code.
#include <iostream>
using namespace std;
int main () {
string fold;
cout << "Enter a word: ";
cin >> fold;
string temp;
string backwards;
string wrap;
for (unsigned int i = 0; i < fold.length(); i++){
temp = temp + fold[i];
}
backwards= string(temp.rbegin(),temp.rend());
for(unsigned int i = 0; i < temp.length(); i++) {
wrap = fold.replace(backwards[i]);
}
cout << wrap;
}
Thanks
#Supreme, there are number of ways to do your task and I'm going to post one of them. But as #John had pointed you must try your own to get it done because real programming is all about practicing a lot. Use this solution just as a reference of one possibility and find many others.
int main()
{
string in;
cout <<"enter: "; cin >> in;
string fold;
for (int i=0, j=in.length()-1; i<in.length()/2; i++, j--)
{
fold += in[i];
fold += in[j];
}
if( in.length()%2 != 0) // if string lenght is odd, pick the middle
fold += in[in.length()/2];
cout << endl << fold ;
return 0;
}
good luck !
There are two approaches to this form of problem, a mathematically exact method would be to create a generator function which returns the number in the correct order.
An easier plan would be to modify the string to solve practically the problem.
Mathematical solution
We want a function which returns the index in the string to add. We have 2 sequences - increasing and decreasing and they are interleaved.
sequence 1 :
0, 1 , 2, 3.
sequence 2
len-1, len-2, len-3, len-4.
Given they are interleaved, we want even values to be from sequence 1 and odd values from sequence 2.
So our solution would be to for a given new index, choose which sequence to use, and then return the next value from that sequence.
int generator( int idx, int len )
{
ASSERT( idx < len );
if( idx %2 == 0 ) { // even - first sequence
return idx/2;
} else {
return (len- (1 + idx/2);
}
}
This can then be called from a function fold...
std::string fold(const char * src)
{
std::string result;
std::string source(src);
for (size_t i = 0; i < source.length(); i++) {
result += source.at(generator(i, source.length()));
}
return result;
}
Pratical solution
Although less efficient, this can be easier to think about. We are taking either the first or the last character of a string. This we will do using string manipulation to get the right result.
std::string fold2(const char * src)
{
std::string source = src;
enum whereToTake { fromStart, fromEnd };
std::string result;
enum whereToTake next = fromStart;
while (source.length() > 0) {
if (next == fromStart) {
result += source.at(0);
source = source.substr(1);
next = fromEnd;
}
else {
result += source.at(source.length() - 1); // last char
source = source.substr(0, source.length() - 1); // eat last char
next = fromStart;
}
}
return result;
}
You can take advantage of the concept of reverse iterators to write a generic algorithm based on the solution presented in Usman Riaz answer.
Compose your string picking chars from both the ends of the original string. When you reach the center, add the char in the middle if the number of chars is odd.
Here is a possible implementation:
#include <iostream>
#include <string>
#include <vector>
#include <utility>
#include <algorithm>
#include <iterator>
template <class ForwardIt, class OutputIt>
OutputIt fold(ForwardIt source, ForwardIt end, OutputIt output)
{
auto reverse_source = std::reverse_iterator<ForwardIt>(end);
auto reverse_source_end = std::reverse_iterator<ForwardIt>(source);
auto source_end = std::next(source, std::distance(source, end) / 2);
while ( source != source_end )
{
*output++ = *source++;
*output++ = *reverse_source++;
}
if ( source != reverse_source.base() )
{
*output++ = *source;
}
return output;
}
int main() {
std::vector<std::pair<std::string, std::string>> tests {
{"", ""}, {"a", "a"}, {"stack", "sktca"}, {"steack", "sktcea"}
};
for ( auto const &test : tests )
{
std::string result;
fold(
std::begin(test.first), std::end(test.first),
std::back_inserter(result)
);
std::cout << (result == test.second ? " OK " : "FAILED: ")
<< '\"' << test.first << "\" --> \"" << result << "\"\n";
}
}