I'm working on a function and it currently works as is.
void ceasarShift( CeasarText& text, int amount ) {
// Bound amount to the number of characters in the alphabet
amount %= 26;
for ( std::size_t i = 0; i < text._originalText.length(); i++ ) {
char c = text._originalText[i] + amount;
text._shiftedText += c;
}
}
The class that this function takes is a simple class that has 2 std::string member variables. When this class is passed by reference where this is a friend function to that class, it takes the original string that is already set and uses that to populate the shifted string which is empty before this function sees it. When the for loop is done, the function is finished and the class object is returned back by reference with a newly updated shifted string and the original is unmodified.
I was thinking of maybe using std::transform and a lambda to do the same task. This is what I have tried.
void ceasarShift( CeasarText& text, int amount ) {
// Bound amount to the number of characters in the alphabet
amount %= 26;
/*for ( std::size_t i = 0; i < text._originalText.length(); i++ ) {
char c = text._originalText[i] + amount;
text._shiftedText += c;
}*/
std::transform( text._originalText.begin(), text._originalText.end(),
text._shiftedText.begin(),
[amount]( unsigned char c ) -> unsigned char { return c + amount; }
);
}
And I end up getting a Debug Assertion Failed! Window message Expression: cannot seek string iterator past end
I'm wondering if my lambda is wrong or if I need to use something else other than std::transform.
Edit
I have also tried this and this works like the first loop:
{
amount %= 26;
for ( auto& c : text._originalText )
text._shiftedText += (c + amount);
}
I just can not seem to get the lambda to work properly as the predicate to std::transform.
The problem is not your lambda. The difference between your two snippets, is that your for loop appends elements to your string, using operator+=, but your transform assumes that the elements already exist. If you want to append to the string with transform, then you will need to use an insert iterator. Specifically a back insert iterator. So instead of passing text._shiftedText.begin(), pass std::back_inserter(text._shiftedText).
std::transform( text._originalText.begin(), text._originalText.end(),
std::back_inserter(text._shiftedText),
[amount]( unsigned char c ) -> unsigned char { return c + amount; }
);
According to this link, The behavior of std::transform is equivalent to:
template <class InputIterator, class OutputIterator, class UnaryOperator>
OutputIterator transform (InputIterator first1, InputIterator last1,
OutputIterator result, UnaryOperator op)
{
while (first1 != last1) {
*result = op(*first1); // or: *result=binary_op(*first1,*first2++);
++result; ++first1;
}
return result;
}
which means the container of OutputIterator should reserve enough space, otherwise, it will out of range when ++result
Related
I have a finite array whose elements are only -1,0 or 1. I want to find the index of Nth occurrence of a number (say 0).
I can iterate through the entire array, but I'm looking for a faster approach. I can think of using Binary Search, but having trouble modelling the algorithm. How do I proceed with Binary Search in this case?
You cannot do this without at least one pass of O(N) pre-processing. From an standpoint of information theory alone, you must have knowledge of elements [0:k-1] to know whether element [k] is the one you want.
If you're going to make this search many times, then you can make a simple linear pass over the array, counting each element as you go. Store the indices in a 2-D array, so you can directly index whatever occurrence you want.
For instance, given [-1 0 1 1 -1 -1 0 0 0 -1 1], you can convert this to a 3xN array, idx
[[0 4 5 9]]
[[1 6 7 8]]
[[2 3 10]]
The Nth occurrence of element I is idx[I+1][N-1].
After that initial O(N) pass, your look-up is O(1) time, using O(N) space.
The OP stated that the ordered structure is important and that the vector or array is unsorted. To the best of my knowledge there is no faster search algorithm than linear for unsorted data. Here are a few links for references:
gamedev.net
quora.com
discuss.codechef.com
ubuntuforums.org
With the above links for references; this should be enough evidence to conclude that if the data in the array or vector is unsorted and must maintain its structure, then there is but no choice to use linear iteration, it may be possible to use a hashing technique, but that can still be tricky, using binary search will only work on sorted data in most cases.
- Here is a good linear algorithm to find the Nth occurrence of T in data.
To solve your problem of finding the Nth occurrence of element T in a given unsorted array, vector or container you can use this simple function template:
It takes 3 parameters:
a const reference to the container that is populated with data
a const unsigned value N where N is the Nth occurrence.
and a const template type T that you are searching for.
It returns an unsigned value for the index location within the container
of the Nth occurrence of element T
template<class T>
unsigned RepititionSearch( const std::vector<T>& data, const unsigned N, const T element ) {
if ( data.empty() || N < 0 || N >= data.size() ) {
return -1;
}
unsigned counter = 0;
unsigned i = 0;
for ( auto e : data ) {
if ( element == e ) {
++counter;
i++;
} else {
i++;
}
if ( counter == N ) {
return i - 1;
}
}
return -1;
}
Break down of the algorithm
It first does some sanity checks:
It checks to see if the container is empty
It checks the value N to see if it is within bounds of [0,container.size())
If any of these fail, it returns -1; in production code this might throw
an exception or an error
We then have a need for 2 incrementing counters:
1 for the current index location
1 for the number of occurrences of element T
We then use a simplified for loop using c++11 or higher
We go through each e in data
We check to see if the element passed into the function is equal to
the current e in data
If the check passes or is true we then pre-increment counter and
post-increment i otherwise we only want to post-increment i
After incrementing the counters we then check to see if the current
counter is equal to the Nth value passed into the function
If the check passes we return the value of i-1 since containers are 0 based
If the check fails here we then continue to the next iteration of the loop and repeat the process
If after all e in data has been checked and there are no occurrences
of T == e or N != counter then we leave the for loop and the function
returns a -1; in production code this might throw an exception or return an error.
The worst case scenario here is either there are no finds, or the Nth occurrence of T happens to be the very last e in data where this will yield O(N) which is linear, and for basic containers this should be efficient enough. If the containers have array indexing capabilities their item access should be O(1) constant if you know which index location you want.
Note: This would be the answer that I feel should solve the problem, if you are interested in a breakdown of how the design process of designing or modeling such an algorithm works you can refer to my reference answer here
AFAIK I do not think there is a better way to do this with unsorted array data, but don't quote me on it.
Since you are looking to search through an array, a vector or some container where the search in question pertains to the index location of some element T based on its Nth occurrence within its container this post may be of some help to you:
According to your question as well as some of the comments in regards to it where you explicitly stated that your container is Unsorted while you were thinking of using a binary search and were having trouble with the process of modeling an algorithm:
This post here serves as an example of the development process towards the design of an algorithm in which it may help you achieve what you are looking for:
The search algorithm here is a linear one, where a binary search will not be suitable to your current needs:
This same process of building an algorithm can be applied to other types of algorithms including, binary searches, hash tables, etc.
- 1st Build
struct Index {
static unsigned counter; // Static Counter
unsigned location; // index location of Nth element
unsigned count; // How many of this element up to this point
Index() : location( 0 ), count( 0 ) {}
};
unsigned Index::counter = 0;
// These typedefs are not necessarily needed;
// just used to make reading of code easier.
typedef Index IndexZero;
typedef Index IndexPos1;
typedef Index IndexNeg1;
template<class T>
class RepititionSearch {
public:
// Some Constants to compare against: don't like "magic numbers"
const T NEG { -1 };
const T ZERO { 0 };
const T POS { 1 };
private:
std::vector<T> data_; // The actual array or vector of data to be searched
std::vector<Index> indices_; // A vector of Indexes - record keeping to prevent multiple full searches.
public:
// Instantiating a search object requires an already populated container
explicit RepititionSearch ( const std::vector<T>& data ) : data_( data ) {
// make sure indices_ is empty upon construction.
indices_.clear();
}
// method to find the Nth occurrence of object A
unsigned getNthOccurrence( unsigned NthOccurrence, T element ) {
// Simple bounds checking
if ( NthOccurrence < 0 || NthOccurrence >= data.size() ) {
// Can throw error or print message...;
return -1;
}
IndexZero zeros;
IndexPos1 ones;
IndexNeg1 negOnes;
// Clear out the indices_ so that each consecutive call is correct
indices_.clear();
unsigned idx = 0;
for ( auto e : data_ ) {
if ( element == e && element == NEG ) {
++negOnes.counter;
negOnes.location = idx;
negOnes.count = negOnes.counter;
indices_.push_back( negOnes );
}
if ( element == e && element == ZERO ) {
++zeros.counter;
zeros.location = idx;
zeros.count = zeros.counter;
indices_.push_back( zeros );
}
if ( element == e && element == POS ) {
++ones.counter;
ones.location = idx;
ones.count = ones.counter;
indices_.push_back( ones );
}
idx++;
} // for each T in data_
// Reset static counters
negOnes.counter = 0;
zeros.counter = 0;
ones.counter = 0;
// Now that we saved a record: find the nth occurance
// This will not search the full vector unless it is last element
// This has early termination. Also this vector should only be
// a percentage of the original data vector's size in elements.
for ( auto index : indices_ ) {
if ( index.count == NthOccurrence) {
// We found a match
return index.location;
}
}
// Not Found
return -1;
}
};
int main() {
// using the sample array or vector from User: Prune's answer!
std::vector<char> vec{ -1, 0, 1, 1, -1, -1, 0, 0, 0, -1, 1 };
RepititionSearch <char> search( vec );
unsigned idx = search.getNthOccurrence( 3, 1 );
std::cout << idx << std::endl;
std::cout << "\nPress any key and enter to quit." << std::endl;
char q;
std::cin >> q;
return 0;
}
// output:
10
The value of 10 is the correct answer as the 3rd occurrence of the value 1 is at location 10 in the original vector since vectors are 0 based. The vector of indices is only used as book keeping for faster search.
If you noticed I even made this a class template to accept any basic type T that'll be stored in std::vector<T> as long as T is comparable, or has operators defined for it.
AFAIK I do not think that there is any other searching method that is faster than this for the type of search that you are striving for, but don't quote me on it. However I think I can optimize this code a little more... just need some time to look at it closer.
This may appear to be a bit crazy but this does work: just a bit of fun playing around with the code
int main() {
std::cout <<
RepititionSearch<char>( std::vector<char>( { -1, 0, 1, 1, -1, -1, 0, 0, 0, -1, 1 } ) ).getNthOccurrence( 3, 1 )
<< std::endl;
}
It can be done on a single line & printed to the console without creating an instance of class.
- 2nd Build
Now this may not necessarily make the algorithm faster, but this would clean up the code a bit for readability. Here I removed the typedefs, and just by using a single version of the Index struct in the 3 if statements you will see duplicate code so I decided to make a private helper function for that and this is how simple the algorithm looks for clear readability.
struct Index {
unsigned location;
unsigned count;
static unsigned counter;
Index() : location(0), count(0) {}
};
unsigned Index::counter = 0;
template<class T>
class RepitiionSearch {
public:
const T NEG { -1 };
const T ZERO { 0 };
const T POS { 1 };
private:
std::vector<T> data_;
std::vector<Index> indices_;
public:
explicit RepititionSearch( const std::vector<T>& data ) : data_( data )
indices_.clear();
}
unsigned getNthOccurrence( unsigned NthOccurrence, T element ) {
if ( NthOccurrence < 0 || NthOccurrence >= data.size() ) {
return -1;
}
indices_.clear();
Index index;
unsigned i = 0;
for ( auto e : data_ ) {
if ( element == e && element == NEG ) {
addIndex( index, i );
}
if ( element == e && element == ZERO ) {
addIndex( index, i );
}
if ( element == e && element == POS ) {
addIndex( index, i );
}
i++;
}
index.counter = 0;
for ( auto idx : indices_ ) {
if ( idx.count == NthOccurrence ) {
return idx.location;
}
}
return -1;
}
private:
void addIndex( Index& index, unsigned inc ) {
++index.counter;
index.location = inc;
index.count = index.counter;
indices_.push_back( index );
}
};
- 3rd Build
And to make this completely generic to find any Nth occurrence of any element T the above can be simplified and reduced down to this: I also removed the static counter from Index and moved it to the private section of RepititionSearch, it just made more sense to place it there.
struct Index {
unsigned location;
unsigned count;
Index() : location(0), count(0) {}
};
template<class T>
class RepititionSearch {
private:
static unsigned counter_;
std::vector<T> data_;
std::vector<Index> indices_;
public:
explicit RepititionSearch( const std::vector<T>& data ) : data_( data ) {
indices_.clear();
}
unsigned getNthOccurrence( unsigned NthOccurrence, T element ) {
if ( NthOccurrence < 0 || NthOccurrence >= data_.size() ) {
return -1;
}
indices_.clear();
Index index;
unsigned i = 0;
for ( auto e : data_ ) {
if ( element == e ) {
addIndex( index, i );
}
i++;
}
counter_ = 0;
for ( auto idx : indices_ ) {
if ( idx.count == NthOccurrence ) {
return idx.location;
}
}
return -1;
}
private:
void addIndex( Index& index, unsigned inc ) {
++counter_;
index.location = inc;
index.count = counter_;
indices_.push_back( index );
}
};
template<class T>
unsigned RepititionSearch<T>::counter_ = 0;
- 4th Build
I have also done this same algorithm above without the need or dependency of needing a vector just to hold index information. This version doesn't need the Index struct at all and doesn't need a helper function either. It looks like this:
template<class T>
class RepititionSearch {
private:
static unsigned counter_;
std::vector<T> data_;
public:
explicit RepititionSearch( const std::vector<T>& data ) : data_( data ) {}
unsigned getNthOcc( unsigned N, T element ) {
if ( N < 0 || N >= data_.size() ) {
return -1;
}
unsigned i = 0;
for ( auto e : data_ ) {
if ( element == e ) {
++counter_;
i++;
} else {
i++;
}
if ( counter_ == N ) {
counter_ = 0;
return i-1;
}
}
counter_ = 0;
return -1;
}
};
template<class T>
unsigned RepititionSearch<T>::counter_ = 0;
Since we were able to remove the dependency of the secondary vector and removed the need for a helper function; we don't even need a class at all to hold the container; we can just write a function template that takes a vector and apply the same algorithm. Also there is no need for a static counter with this version.
- 5th Build
template<class T>
unsigned RepititionSearch( const std::vector<T>& data, unsigned N, T element ) {
if ( data.empty() || N < 0 || N >= data.size() ) {
return -1;
}
unsigned counter = 0;
unsigned i = 0;
for ( auto e : data ) {
if ( element == e ) {
++counter;
i++;
} else {
i++;
}
if ( counter == N ) {
return i - 1;
}
}
return -1;
}
Yes this is a lot to take in; but these are the steps that are involved in the process of writing and designing an algorithm and refining it down to simpler code. As you have seen I have refined this code about 5 times. I went from using a struct, a class, typedefs, and a static member with multiple stored containers, to removing the typedefs and putting the repeatable code into a helper function, to removing the dependency of a secondary container & the helper function, down to not even needing a class at all and just creating a function that does what it is supposed to do.
You can apply a similar approach to these steps into building a function that does what you want or need it to do. You can use the same process to write a function that will do a binary search, hash table, etc.
I am trying to solve the following question https://www.interviewbit.com/problems/largest-number/ : Given a list of non negative integers, arrange them such that they form the largest number.
For example:
Given [3, 30, 34, 5, 9], the largest formed number is 9534330.
Note: The result may be very large, so you need to return a string instead of an integer.
I have been able to solve it and implemented it, using comparison based sorting technique. That is, given two numbers X and Y, I compare two numbers XY (Y appended at the end of X) and YX (X appended at the end of Y). If XY is larger, then X should come before Y in output, else Y should come before. The following is the code:
string Solution::largestNumber(const vector<int> &A) {
// Do not write main() function.
// Do not read input, instead use the arguments to the function.
// Do not print the output, instead return values as specified
// Still have a doubt. Checkout www.interviewbit.com/pages/sample_codes/ for more details
vector<string> myvec;
for (int i = 0; i < A.size(); i++)
{
string s = to_string(A[i]);
myvec.push_back(s);
}
sort(myvec.begin(),myvec.end(),mycomp());
string s = "";
auto it = myvec.begin();
while (it != myvec.end())
{
string p = *it;
s = s + p;
it++;
}
return s;
}
struct mycomp
{
inline bool operator() (const string &p1, const string &p2)
{
string s1 = p1.append(p2);
string s2 = p2.append(p1);
if (s1.compare(s2) < 0)
return false;
else
return true;
}
};
But, the problem is, I have to merge the two functions into a single one because I just have to implement the single function. I cannot define one more function since I have no control over the entire piece of code (look at the link's submission part). Therefore, my ask is, how can I use the comparator by defining it inside the function string Solution::largestNumber(const vector<int> &A). Thanks!
This is a perfect place for a lambda.
sort(myvec.begin(), myvec.end(), [](const string &p1, const string &p2) {
string s1(p1 + p2);
string s2(p2 + p1);
return s1.compare(s2) >= 0;
});
I changed your code to not call append() on the strings, since you accept them as references to const objects, and p1.append(p2) tries to modify p1, but that's not allowed on a const object. Further, avoid constructs like if(x) return true else return false; and instead just return x;
Also, this
string s = "";
auto it = myvec.begin();
while (it != myvec.end())
{
string p = *it;
s = s + p;
it++;
}
return s;
Can be condensed to:
string s;
for (auto const& e : myvec)
s += e;
return s;
(Assuming you have a c++11 compiler or later)
Recently I saw some example code on how to use boost::zip_iterator. However, I can't figure out how it works. Here is the code:
class to_hex2
{
private:
vector<unsigned char> &v;
char trans(const char c) const
{
if(c >= 'a')
return c - 'a' + 10;
else if(c >= 'A')
return c - 'A' + 10;
else
return c - '0';
}
public:
to_hex2(vector<unsigned char> &_v):
v(_v){}
typedef boost::tuples::tuple<const char&,const char&> Tuple;
void operator()(Tuple const &t) const
{
static char tmp;
tmp = trans(t.get<0>()) * 0x10;
tmp += trans(t.get<1>());
v.push_back(tmp);
}
};
int main()
{
char s[] = "1234aBcD";
vector<unsigned char> v;
typedef step_iterator<const char*> si_t;
for_each(
boost::make_zip_iterator(
boost::tuples::make_tuple(si_t(s),si_t(s+1))),
boost::make_zip_iterator(
boost::tuples::make_tuple(si_t(s+8),si_t(s+9))),
to_hex2(v));
std::copy(
v.begin(),v.end(),std::ostream_iterator<unsigned char>(cout," "));
std::cout<<std::endl<<"v.size="<<v.size();
return 0;
}
step_iterator is an iterator that iterates two steps instead of one.
My first question is: Is it OK to write s+9 since the index of array s is up to 8(including '\0':-) )? The code seems to run properly although.
My second question is: Since zip_iterator makes it possible to iterate over a vector concurrently, does that mean the result is random? The result I see is constant, in the following picture:
Last but not least, could someone please tell me how is the result generated( what's the meaning of it) since there is no up-down arrow in ASCII codes( I googled it and saw it here).
It is ok to point one-past-the-end of an array, as long as you don't dereference the pointer. This is very useful because C++ uses half-open ranges, where the last element is excluded.
In the code you posted, s+9 points one-past-the-end of s, but is never dereferenced, so the behavior is well-defined.
Regarding your second question: no, the result of this code is not random. The elements will be iterated over in order, from first to last. When the documentation states that zip_iterator allows parallel iteration over a sequence, it does not mean that the iteration will be performed concurrently by several threads or whatever, it only means that each iteration will advance several iterators instead of only one. Here is a possible implementation of for_each:
template <typename InputIterator, typename Func>
void for_each(InputIterator first, InputIterator last, Func f)
{
while (first != last)
{
f(*first);
++first;
}
}
As you see, for_each works on a single iterator. If you need to iterate over two sequences at a time, then you can use zip_iterator, which encapsulates several iterators. Its operator* returns multiple values (a tuple), and its operator++s increments all the iterators, advancing them simultaneously.
To better understand what is going on in your code, here is a streamlined version, without zip_iterator and for_each:
class to_hex2
{
private:
vector<unsigned char> &v;
char trans(const char c) const
{
if(c >= 'a')
return c - 'a' + 10;
else if(c >= 'A')
return c - 'A' + 10;
else
return c - '0';
}
public:
to_hex2(vector<unsigned char> &_v):
v(_v){}
void operator()(const char &first, const char &second) const
{
static char tmp;
tmp = trans(first) * 0x10;
tmp += trans(second);
v.push_back(tmp);
}
};
int main()
{
char s[] = "1234aBcD";
vector<unsigned char> v;
to_hex2 transformer(v);
char *first = s;
char *second = s + 1;
for ( ; first != s + 8 && second != s + 9 ; first += 2, second += 2)
{
transformer(*first, *second);
}
std::copy(v.begin(),v.end(),
std::ostream_iterator<unsigned char>(cout," "));
std::cout<<std::endl<<"v.size="<<v.size();
return 0;
}
Hopefully, this should make it clear that zip_iterator is just a convenient way of making several iterators advance at the same time.
Finally, to understand the purpose of this code, you should probably print the result as integers rather than as characters. You should see this:
18 52 171 205
which are the decimal representation of the hexadecimal numbers contained in the original string (1216 = 1810, 3416 = 5210, AB16 = 17110 and CD16 = 20510). So basically, v contains the representation in base 256 of the original hexadecimal string.
I'm having a very odd problem with some code using std::sort. If I replace std::sort by stable_sort the problem goes away.
class Entry
{
public:
Entry() : _date(0), _time(0), _size(0) {}
Entry(unsigned int d, unsigned int t, unsigned int s) : _date(d), _time(t), _size(s) {}
~Entry() {_size=0xfffffffe;}
unsigned int _date, _time, _size;
};
void initialise(std::vector<Entry> &vec)
vec.push_back(Entry(0x3f92, 0x9326, 0x1ae));
vec.push_back(Entry(0x3f92, 0x9326, 0x8a54));
//.... + a large number of other entries
}
static bool predicate(const Entry &e1, const Entry &e2)
{
// Sort by date and time, then size
if (e1._date < e2._date )
return true;
if (e1._time < e2._time )
return true;
return e1._size < e2._size;
}
int main (int argc, char * const argv[]) {
using namespace std;
vector<Entry> vec;
initialise(vec);
sort(vec.begin(), vec.end(), predicate);
vector<Entry>::iterator iter;
for (iter=vec.begin(); iter!=vec.end(); ++iter)
cout << iter->_date << ", " << iter->_time <<
", 0x" << hex << iter->_size << endl;
return 0;
}
The idea is that I sort the data first by date and time then by size. However, depending on the data in the vector, I will end up with 0xfffffffe in the size printed out at the end for the first object, indicating that a destroyed object has been accessed, or a seg fault during the sort.
(Xcode 3.2.4 - 64 bit intel target)
Any ideas anyone??
I suspect it has something to do with my predicate, but I can't see for the life of me what it is....!!
This page seems to refer to the same problem:
http://schneide.wordpress.com/2010/11/01/bug-hunting-fun-with-stdsort/
but the reason he gives (that the predicate needs to define a strict weak ordering) seems to be satisfied here...
Your predicate does not satisfy strict weak ordering criteria. Look at your function and ask yourself, what happens if e1's date comes after e2, but e1's time comes before e2?
I think what your predicate really should be is something like this:
static bool predicate(const Entry &e1, const Entry &e2)
{
// Sort by date and time, then size
return e1._date < e2._date ||
(e1._date == e2._date &&
(e1._time < e2._time ||
(e1._time == e2._time && e1._size < e2._size)));
}
What you wrote - if e1._date>e2._date, the first condition will be false, but the second may still be true and the function will still claim that e1<e2 which is probably not what you want.
Your code needs to be:
static bool predicate(const Entry &e1, const Entry &e2)
{
// Sort by date and time, then size
if (e1._date != e2._date )
return e1._data < e2._date;
if (e1._time != e2._time )
return e1._time < e2._time;
return e1._size < e2._size;
}
If e2's date is after e1, then your version treats goes on to compare the time and size. This is not what you want. This eventually confuses std::sort because if you swap e1 and e2 you will not get a consistent answer.
My question is related to this.
I wanted to perform a sort() operation over the set with the help of a lambda expression as a predicate.
My code is
#include <set>
#include <string>
#include <iostream>
#include <algorithm>
int main() {
using namespace std;
string s = "abc";
set<string> results;
do {
for (int n = 1; n <= s.size(); ++n) {
results.insert(s.substr(0, n));
}
} while (next_permutation(s.begin(), s.end()));
sort (results.begin(),results.end());[](string a, string b)->bool{
size_t alength = a.length();
size_t blength = b.length();
return (alength < blength);
});
for (set<string>::const_iterator x = results.begin(); x != results.end(); ++x) {
cout << *x << '\n';
}
return 0;
}
But the numbers and types of errors were so complex that I couldn't understand how to fix them. Can someone tell me whats wrong with this code.
Edit: Note that Steve Townsend's solution is actually the one you're searching for, as he inlines as a C++0x Lambda what I write as C++03 code below.
Another solution would be to customize the std::set ordering function:
The std::set is already ordered...
The std::set has its own ordering, and you are not supposed to change it once it is constructed. So, the following code:
int main(int argc, char* argv[])
{
std::set<std::string> aSet ;
aSet.insert("aaaaa") ;
aSet.insert("bbbbb") ;
aSet.insert("ccccccc") ;
aSet.insert("ddddddd") ;
aSet.insert("e") ;
aSet.insert("f") ;
outputSet(aSet) ;
return 0 ;
}
will output the following result:
- aaaaa
- bbbbb
- ccccccc
- ddddddd
- e
- f
... But you can customize its ordering function
Now, if you want, you can customize your set by using your own comparison function:
struct MyStringLengthCompare
{
bool operator () (const std::string & p_lhs, const std::string & p_rhs)
{
const size_t lhsLength = p_lhs.length() ;
const size_t rhsLength = p_rhs.length() ;
if(lhsLength == rhsLength)
{
return (p_lhs < p_rhs) ; // when two strings have the same
// length, defaults to the normal
// string comparison
}
return (lhsLength < rhsLength) ; // compares with the length
}
} ;
In this comparison functor, I did handle the case "same length but different content means different strings", because I believe (perhaps wrongly) that the behaviour in the original program is an error. To have the behaviour coded in the original program, please remove the if block from the code.
And now, you construct the set:
int main(int argc, char* argv[])
{
std::set<std::string, MyStringLengthCompare> aSet ;
aSet.insert("aaaaa") ;
aSet.insert("bbbbb") ;
aSet.insert("ccccccc") ;
aSet.insert("ddddddd") ;
aSet.insert("e") ;
aSet.insert("f") ;
outputSet(aSet) ;
return 0 ;
}
The set will now use the functor MyStringLengthCompare to order its items, and thus, this code will output:
- e
- f
- aaaaa
- bbbbb
- ccccccc
- ddddddd
But beware of the ordering mistake!
When you create your own ordering function, it must follow the following rule:
return true if (lhs < rhs) is true, return false otherwise
If for some reason your ordering function does not respect it, you'll have a broken set on your hands.
std::sort rearranges the elements of the sequence you give it. The arrangement of the sequence in the set is fixed, so the only iterator you can have is a const iterator.
You'll need to copy results into a vector or deque (or such) first.
vector sortable_results( results.begin(), results.end() );
You can customize the ordering of the elements in the set by providing a custom predicate to determine ordering of added elements relative to extant members. set is defined as
template <
class Key,
class Traits=less<Key>,
class Allocator=allocator<Key>
>
class set
where Traits is
The type that provides a function
object that can compare two element
values as sort keys to determine their
relative order in the set. This
argument is optional, and the binary
predicate less is the default
value.
There is background on how to use lambda expression as a template parameter here.
In your case this translates to:
auto comp = [](const string& a, const string& b) -> bool
{ return a.length() < b.length(); };
auto results = std::set <string, decltype(comp)> (comp);
Note that this will result in set elements with the same string length being treated as duplicates which is not what you want, as far as I can understand the desired outcome.
sort requires random access iterators which set doesn't provide (It is a bidirectional iterator). If you change the code to use vector it compiles fine.
You cannot sort a set. It's always ordered on keys (which are elements themselves).
To be more specific, std::sort requires random access iterators. The iterators provided by std::set are not random.
Since I wrote the original code you're using, perhaps I can expand on it... :)
struct cmp_by_length {
template<class T>
bool operator()(T const &a, T const &b) {
return a.length() < b.length() or (a.length() == b.length() and a < b);
}
};
This compares by length first, then by value. Modify the set definition:
set<string, cmp_by_length> results;
And you're good to go:
int main() {
using namespace std;
string s = "abc";
typedef set<string, cmp_by_length> Results; // convenience for below
Results results;
do {
for (int n = 1; n <= s.size(); ++n) {
results.insert(s.substr(0, n));
}
} while (next_permutation(s.begin(), s.end()));
// would need to add cmp_by_length below, if I hadn't changed to the typedef
// i.e. set<string, cmp_by_length>::const_iterator
// but, once you start using nested types on a template, a typedef is smart
for (Results::const_iterator x = results.begin(); x != results.end(); ++x) {
cout << *x << '\n';
}
// of course, I'd rather write... ;)
//for (auto const &x : results) {
// cout << x << '\n';
//}
return 0;
}
std::set is most useful to maintain a sorted and mutating list. It faster and smaller to use a vector when the set itself wont change much once it's been built.
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
int main() {
using namespace std;
string s = "abc";
vector<string> results;
do {
for (size_t n = 1; n <= s.size(); ++n) {
results.push_back(s.substr(0, n));
}
} while (next_permutation(s.begin(), s.end()));
//make it unique
sort( results.begin(), results.end() );
auto end_sorted = unique( results.begin(), results.end() );
results.erase( end_sorted, results.end() );
//sort by length
sort (results.begin(),results.end());
[](string lhs, string rhs)->bool
{ return lhs.length() < rhs.length(); } );
for ( const auto& result: results ) {
cout << result << '\n';
}
}
I used the classic, sort/unique/erase combo to make the results set unique.I also cleaned up your code to be a little bit more c++0x-y.