c++ customized map key/value not in order - c++

I built the following map using a self-defined struct.
#include <iostream>
#include <vector>
#include <map>
struct keys {
int first;
int second;
int third;
};
struct keyCompare
{
bool operator()(const keys& k1, const keys& k2)
{
//return (k1.first<k2.first && k1.second<k2.second && k1.third<k2.third);
return (k1.first<k2.first || k1.second<k2.second || k1.third<k2.third);
//return (k1.first<k2.first || (k1.first==k2.first && k1.second<k2.second) || (k1.first==k2.first
// && k1.second==k2.second && k1.third<k2.third));
}
};
int main()
{
keys mk, mk1;
int len = 4;
//int myints1[9] = {1,2,3,4,5,6, 7,8,9};
int myints1[12] = {1,2,3,4,5,6,1,2,3,1,2,3};
std::vector<int> input1(myints1, myints1+12);
std::map<keys, int, keyCompare> c2int;
for (int i = 0; i < len; i++) {
mk.first = input1[i*3];
mk.second = input1[i*3+1];
mk.third = input1[i*3+2];
c2int[mk] = i;
}
for (int i = 0; i < len;i++) {
mk1.first = input1[i*3];
mk1.second = input1[i*3+1];
mk1.third = input1[i*3+2];
std::cout << "map content " << c2int[mk1] << "\n";
}
return 0;}
The code works as expected for non-repeated keys like {1,2,3,4,5,6,7,8,9}. The return is
map content is 0
map content is 1
map content is 2
but when there are repeated patterns, e.g., keys are {1,2,3,4,5,6,1,2,3}. The print out is
map content is 2
map content is 1
map content is 2
while I was expecting
map content is 0
map content is 1
map content is 0
since key {1,2,3} has already assigned value 0. But the compare function seems modify this key to value 2 instead of 0. I tried different compare function but none of them shows expected output. I think I had missed something in this approach. Can someone explain? Thanks

This comparator is incorrect:
bool operator()(const keys& k1, const keys& k2)
{
return (k1.first<k2.first || k1.second<k2.second || k1.third<k2.third);
}
Consider {1,4,9} vs {2,3,4}. {1,4,9} < {2,3,4} because of the first comparison, but then {2,3,4} < {1,4,9} because of the second! That's clearly not what you intended! Besides, operator< must be asymmetric in order to be a StrictWeakOrdering, which is what is required for std::map.
You have to deal with the keys in order:
bool operator()(const keys& k1, const keys& k2) {
if (k1.first != k2.first) {
return k1.first < k2.first;
}
else if (k1.second != k2.second) {
return k1.second < k2.second;
}
else {
return k1.third < k2.third;
}
}

Your keyCompare is not valid for a std::map.
It needs to return true for (a,b) if a should be ordered before b.
You have written a function that could return true for (a,b) and (b,a). This violates strict weak ordering.

Related

How do I Optimize my C++ key-value program to have a faster runtime?

This is a2.hpp, and is the program that can be edited, as far as I know the code is correct, just too slow. I am honestly lost here, I know my for loops are probably whats slowing me down so much, maybe use an iterator?
// <algorithm>, <list>, <vector>
// YOU CAN CHANGE/EDIT ANY CODE IN THIS FILE AS LONG AS SEMANTICS IS UNCHANGED
#include <algorithm>
#include <list>
#include <vector>
class key_value_sequences {
private:
std::list<std::vector<int>> seq;
std::vector<std::vector<int>> keyref;
public:
// YOU SHOULD USE C++ CONTAINERS TO AVOID RAW POINTERS
// IF YOU DECIDE TO USE POINTERS, MAKE SURE THAT YOU MANAGE MEMORY PROPERLY
// IMPLEMENT ME: SHOULD RETURN SIZE OF A SEQUENCE FOR GIVEN KEY
// IF NO SEQUENCE EXISTS FOR A GIVEN KEY RETURN 0
int size(int key) const;
// IMPLEMENT ME: SHOULD RETURN POINTER TO A SEQUENCE FOR GIVEN KEY
// IF NO SEQUENCE EXISTS FOR A GIVEN KEY RETURN nullptr
const int* data(int key) const;
// IMPLEMENT ME: INSERT VALUE INTO A SEQUENCE IDENTIFIED BY GIVEN KEY
void insert(int key, int value);
}; // class key_value_sequences
int key_value_sequences::size(int key) const {
//checks if the key is invalid or the count vector is empty.
if(key<0 || keyref[key].empty()) return 0;
// sub tract 1 because the first element is the key to access the count
return keyref[key].size() -1;
}
const int* key_value_sequences::data(int key) const {
//checks if key index or ref vector is invalid
if(key<0 || keyref.size() < static_cast<unsigned int>(key+1)) {
return nullptr;
}
// ->at(1) accesses the count (skipping the key) with a pointer
return &keyref[key].at(1);
}
void key_value_sequences::insert(int key, int value) {
//checks if key is valid and if the count vector needs to be resized
if(key>=0 && keyref.size() < static_cast<unsigned int>(key+1)) {
keyref.resize(key+1);
std::vector<int> val;
seq.push_back(val);
seq.back().push_back(key);
seq.back().push_back(value);
keyref[key] = seq.back();
}
//the index is already valid
else if(key >=0) keyref[key].push_back(value);
}
#endif // A2_HPP
This is a2.cpp, this just tests the functionality of a2.hpp, this code cannot be changed
// DO NOT EDIT THIS FILE !!!
// YOUR CODE MUST BE CONTAINED IN a2.hpp ONLY
#include <iostream>
#include "a2.hpp"
int main(int argc, char* argv[]) {
key_value_sequences A;
{
key_value_sequences T;
// k will be our key
for (int k = 0; k < 10; ++k) { //the actual tests will have way more than 10 sequences.
// v is our value
// here we are creating 10 sequences:
// key = 0, sequence = (0)
// key = 1, sequence = (0 1)
// key = 2, sequence = (0 1 2)
// ...
// key = 9, sequence = (0 1 2 3 4 5 6 7 8 9)
for (int v = 0; v < k + 1; ++v) T.insert(k, v);
}
T = T;
key_value_sequences V = T;
A = V;
}
std::vector<int> ref;
if (A.size(-1) != 0) {
std::cout << "fail" << std::endl;
return -1;
}
for (int k = 0; k < 10; ++k) {
if (A.size(k) != k + 1) {
std::cout << "fail";
return -1;
} else {
ref.clear();
for (int v = 0; v < k + 1; ++v) ref.push_back(v);
if (!std::equal(ref.begin(), ref.end(), A.data(k))) {
std::cout << "fail 3 " << A.data(k) << " " << ref[k];
return -1;
}
}
}
std::cout << "pass" << std::endl;
return 0;
} // main
If anyone could help me improve my codes efficiency I would really appreciate it, thanks.
First, I'm not convinced your code is correct. In insert, if they key is valid you create a new vector and insert it into sequence. Sounds wrong, as that should only happen if you have a new key, but if your tests pass it might be fine.
Performance wise:
Avoid std::list. Linked lists have terrible performance on today's hardware because they break pipelineing, caching and pre-fetching. Always use std::vector instead. If the payload is really big and you are worried about copies use std::vector<std::unique_ptr<T>>
Try to avoid copying vectors. In your code you have keyref[key] = seq.back() which copies the vector, but should be fine since it's only one element.
Otherwise there's no obvious performance problems. Try to benchmark and profile your program and see where the slow parts are. Usually there's one or two places that you need to optimize and get great performance. If it's still too slow, ask another question where you post your results so that we can better understand the problem.
I will join Sorin in saying don't use std::list if avoidable.
So you use key as direct index, where does it say it is none-negative? where does it say its less than 100000000?
void key_value_sequences::insert(int key, int value) {
//checks if key is valid and if the count vector needs to be resized
if(key>=0 && keyref.size() < static_cast<unsigned int>(key+1)) {
keyref.resize(key+1); // could be large
std::vector<int> val; // don't need this temporary.
seq.push_back(val); // seq is useless?
seq.back().push_back(key);
seq.back().push_back(value);
keyref[key] = seq.back(); // we now have 100000000-1 empty indexes
}
//the index is already valid
else if(key >=0) keyref[key].push_back(value);
}
Can it be done faster? depending on your key range yes it can. You will need to implement a flat_map or hash_map.
C++11 concept code for a flat_map version.
// effectively a binary search
auto key_value_sequences::find_it(int key) { // type should be iterator
return std::lower_bound(keyref.begin(), keyref.end(), [key](const auto& check){
return check[0] < key; // key is 0-element
});
}
void key_value_sequences::insert(int key, int value) {
auto found = find_it(key);
// at the end or not found
if (found == keyref.end() || found->front() != key) {
found = keyref.emplace(found, key); // add entry
}
found->emplace_back(value); // update entry, whether new or old.
}
const int* key_value_sequences::data(int key) const {
//checks if key index or ref vector is invalid
auto found = find_it(key);
if (found == keyref.end())
return nullptr;
// ->at(1) accesses the count (skipping the key) with a pointer
return found->at(1);
}
(hope I got that right ...)

Sort structure array in alphabetical order

I have a structure array (A[#]) named Sheep (since my task is about sheeps DNR). After I do whatever the task asked I am left with this struct :
struct Sheep
{
string Vardas;
char Fragmentas[CMax];
int atitikme = 0;
};
and inside my it the data is:
(string Vardas) | (char Fragmentas[CMax]) | (int atitikme)
Baltukas TAGCTT 3
Bailioji ATGCAA 3
Smarkuolis AATGAA 1
(char Fragmentas[CMax] won't be using so u don't have to look at it, I only named it to make it clear).
ALL of this data comes from U2.txt file and cant be manually typed in a code.
All its left to do is to sort it by these rules:
It goes from bigger to smaller by 'int atitikme'.
IF 'int atitikme' is equal then it will have to sort by 'A[#].Vardas in a in alphabetical order.
To sort it by 'int atitikme' I created a code:
string q;
char w[20];
int e;
for (int o = 0; o < n-1; o++)
{
for (int p = o+1; p < n-1; p++)
{
if (A[p].atitikme > A[o].atitikme)
{
// - Vardo Keitimas
q = A[o].Vardas;
A[o].Vardas = A[p].Vardas;
A[p].Vardas = q;
// - A[#].atitikme keitimas
e = A[o].atitikme;
A[o].atitikme = A[p].atitikme;
A[p].atitikme = e;
// - DNR farkmentu keitimas
for (int r = 0; r < m; r++)
{
w[r] = A[o].Fragmentas[r];
A[o].Fragmentas[r] = A[p].Fragmentas[r];
A[p].Fragmentas[r] = w[r];
}
}
}
}
n = 4 | m = 6
How/what do i need to add to this code to make it go:
else if (A[p].atitikme == A[o].atitikme)
{
<code>
}
That if 'atitikme' is == to another 'atitikme' then A[p].Vardas and A[o].Vardas has to be sorted in an alphabetical order. but only those 2 from the whole array.
OR if its too hard to understand what I meant, could anyone post a code, in the answer box, were it would sort in a alphabetical order between 2 string's?
NOTE:
the whole line data
(string Vardas) (char Fragmentas[CMax]) (int atitikme)
has to stay the same, only the place in the line has to be diffirent and sorted by those rules I mentioned before.
The output should be:
Bailioji 3
Baltukas 3
Smarkuolis 1
EDIT:
My current output is:
Baltukas 3
Bailioji 3
Smarkuolis 1
P.s. The task allows to use everything as-long as its C++ and does not have to create, or read, any other file.
Here I have used std::vector<> instead of array to store the sheeps.
Secondly, using std::sort() and a lambda function, you can easily mention how you want to sort the elements in the std::vector<>/ Sheeps. That would be the easiest way to approach.
Here is the live code, in case of reviewing: https://www.ideone.com/ay7TWU
#include <iostream>
#include <vector>
#include <algorithm>
struct Sheep
{
std::string Vardas;
std::vector<char> Fragmentas;
int atitikme;
};
int main()
{
std::vector<Sheep> vec =
{
{"Baltukas", {'T','A','G','C','T','T'}, 3},
{"Bailioji", {'A','T','G','C','A','A'}, 3},
{"Smarkuolis",{'A','A','T','G','A','A'}, 1},
{"Hmarkuolis",{'A','A','T','G','A','A'}, 1},
{"Kmarkuolis",{'A','A','T','G','A','A'}, 2}
};
std::sort(vec.begin(), vec.end(), [](const Sheep& lhs, const Sheep& rhs)
{
return (lhs.atitikme == rhs.atitikme) ?
lhs.Vardas < rhs.Vardas: // if atitikme's of sheeps are equal
lhs.atitikme > rhs.atitikme; // if atitikme's of sheeps are not equal
});
for (const auto& it: vec)
std::cout << it.Vardas << " " << it.atitikme << "\n";
return 0;
}
The output:
Bailioji 3
Baltukas 3
Kmarkuolis 2
Hmarkuolis 1
Smarkuolis 1
The best is to solve your problem one by one.
First - define the sorting order - see doc about - e.g. in std::less
So, you need functor class that defines your sorting order:
class SheepOrder
{
public:
bool operator() ( const Sheep& left, const Sheep& right) const
{
// It goes from bigger to smaller by 'int atitikme'.
if (left.atitikme > right.atitikme)
return true;
if (left.atitikme < right.atitikme)
return false;
//IF 'int atitikme' is equal then it will have to sort it in a in alphabetical order.
// I guess you meant Vardas
return left.Vardas < right.Vardas;
}
};
Now, having defined the order - just use std::sort - it can be used with arrays - no problem:
Sheep sheeps[100];
// ..
std::sort(std::begin(sheeps), std::end(sheeps), SheepOrder{});
or:
void sortSheeps(Sheep* array, std::size_t numOFSheeps)
{
std::sort(array, array + numOfSheeps, SheepOrder{});
}
You can also use std::tuple to make it easier to define sorting order (tuple has operator < by default if their elements have this operator too):
class SheepOrder
{
public:
bool operator() ( const Sheep& left, const Sheep& right) const
{
return tieMembersForSorting(left) < tieMembersForSorting(right);
}
private:
static auto tieMembersForSorting( const Sheep& object)
{
return std::make_tuple(-object.atitikme, // - to revert order
std::ref(object.Vardas)); // ref - to not make copy of string
}
};
With tieMembersForSorting defined as free function - lambda could be used as well (as it will be just one liner):
inline auto tieMembersForSorting( const Sheep& object)
{
return std::make_tuple(-object.atitikme, // - to revert order
std::ref(object.Vardas)); // ref - to not make copy of string
}
std::sort(begin(...), end(...), [](Sheep const& left, Sheep const& right)
{ return tieMembersForSorting(left) < tieMembersForSorting(right); });
https://en.cppreference.com/w/cpp/algorithm/sort shows you how to use std::sort.
You write a function bool less_than(const Sheep& a, const Sheep& b) that represents the order of two sheep and then simply call std::sort(container.begin(), container.end(), less_than);, with container being something like a vector of Sheep.
Edit: The function written out:
bool less_than(const Sheep& a, const Sheep& b)
{
if(a.atitikme != b.atitikme) return a.atitikme < b.atitikme;
return a.Vardas < b.Vardas;
}

How to convert this C# script to native C++?

I'm beginner in C++. Before I work with C#. Bellow is a C# script. How I can do same these things in native C++?
All I need is:
A list, or similar, have int-int key-value pair
Can auto sort by value. If not, it must be sortable by key and it can get index of a
value (each of my values is definite)
I tried std::map but it don't have built-in sort by value or get key by value. Is C++ have similar thing like sortedlist in c#?
Thank you very much!
public static SortedList<int, int> sortedList1 = new SortedList<int, int>();
static void List_Add(int i) // 0 < i < 1000
{
if (!sortedList1.ContainsValue(i))
sortedList1[Environment.TickCount] = i;
}
static void List_Remove(int i) // 0 < i < 1000
{
if (sortedList1.ContainsValue(i))
sortedList1.RemoveAt(sortedList1.IndexOfValue(i));
}
static int List_toInt()
{
int time = 0;
int keys = 0;
bool modifier = false;
foreach (KeyValuePair<int, int> i in sortedList1)
{
if (i.Value > 90) modifier = true;
if (i.Key - time > 200 | modifier | keys > 1000)
{
keys = keys * 1000 + i.Value;
time = i.Key;
}
}
return keys;
}
You seem to be doing the wrong way round as usually things are sorted using keys and queries are done using the key not using the value. However, it seems a std::map<int,int> will help you here. Simply use your value as a key of the map and your key as value(so that you can query using the value). Use multimap if duplicates are allowed.
This are some converter tools:Please visit the following links:
http://sourceforge.net/projects/convetercpptocs/
http://www.tangiblesoftwaresolutions.com/Product_Details/CSharp_to_CPlusPlus_Converter_Details.html
http://cscpp.codeplex.com/
Like that:
#include <map>
#include "Winbase.h"
std::map<int, int> sortedList1;
void List_Add(int i) // 0 < i < 1000
{
if (sortedList1.find(i) == sortedList1.end())
sortedList1.insert(std::make_pair<int, int>(GetTickCount(), i));
}
void List_Remove(int i) // 0 < i < 1000
{
if (sortedList1.find(i) != sortedList1.end())
sortedList1.erase(sortedList1.find(i));
}
int List_toInt()
{
int time = 0;
int keys = 0;
bool modifier = false;
for (std::map<int, int>::const_iterator it = sortedList1.cbegin();
it != sortedList1.cend(); it++)
{
if (it->second > 90) modifier = true;
if (it->first - time > 200 || modifier || keys > 1000)
{
keys = keys * 1000 + it->second;
time = it->first;
}
}
return keys;
}

std map composite key

I have a problem with the operator<() method which is required for a std::map. I'm using a struct as composite key that looks as follows:
struct MyKey {
std::string string1;
std::string string2;
std::string string3;
unsigned int uint1;
friend bool operator<(const MyKey& mk1, const MyKey& mk2)
{
return mk1.string1 < mk2.string1 && mk1.string2 < mk2.string2 &&
mk1.string3 < mk2.string3 && mk1.uint1 < mk2.uint1;
}
}
As introduced I want to use a composite key with 4 values, but I don't know how to achieve this for the operator< method. I observed that only 1 value is stored at a time!
Can anybody tell me how the right condition looks like?
Thanks in advance!
The Standard library's associative containers such as std::map, std::set, std::multiset, std::multimap, std::bitset require that the ordering of elements must follow Strict Weak Ordering, which means your implementation of operator< must follow strict weak ordering. So one implementation could be this:
friend bool operator<(const MyKey& mk1, const MyKey& mk2)
{
if (mk1.string1 != mk2.string1 )
return mk1.string1 < mk2.string1;
else if ( mk1.string2 != mk2.string2)
return mk1.string2 < mk2.string2;
else if (mk1.string3 != mk2.string3)
return mk1.string3 < mk2.string3;
else
return mk1.uint1 < mk2.uint1;
}
Or you can implement it as:
friend bool operator<(const MyKey& mk1, const MyKey& mk2)
{
auto const & t1 = std::tie(mk1.string1, mk1.string2, mk1.string3, mk1.uint1);
auto const & t2 = std::tie(mk2.string1, mk2.string2, mk2.string3, mk2.uint1);
return t1 < t2;
}
In this solution, std::tie function creates two tuples t1 and t1 of the references of the arguments passed to it, and then compare t1 and t2 using overloaded operator< for std::tuple instead. The operator< for tuple compares the elements lexicographically — strict-weak ordering is achieved..
I think you have a problem in that the operator< doesn't necessarily implement strict weak ordering. There are too many combinations where A<B is false and B<A is also false, where A and B are MyKey objects. This is interpreted as A being equal to B.
The problem with your implementation is that it's not stable, consider...
return mk1.string1 < mk2.string1 && mk1.string2 < mk2.string2 &&
mk1.string3 < mk2.string3 && mk1.uint1 < mk2.uint1;
...evaluating { "a", "a", "a", 1 } < { "a", "b", "a", 1 } = a<a && ... = false && ... = false
...but { "a", "b", "a", 1 } < { "a", "a", "a", 1 } = a<a && ... = false && ... = false
So, neither is reported as less than the other, despite them not being equal keys in the map.
A working solution: it's concise and efficient to do each necessary string comparisons only once...
friend bool operator<(const MyKey& mk1, const MyKey& mk2)
{
int x;
return (x = mk1.string1.compare(mk2.string1)) ? x < 0 :
(x = mk1.string2.compare(mk2.string2)) ? x < 0 :
(x = mk1.string3.compare(mk2.string3)) ? x < 0 :
mk1.uint1 < mk2.uint1;
}

Sorting a set<string> on the basis of length

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.