returning vector<string> in c++ - c++

I get: C:\Documents and Settings\ppp\Pulpit\Zadanie3Infix\main.cpp|72|error: conversion from 'std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >*' to non-scalar type 'std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >' requested|
and i don't know why. The function suppose to return vector of strings and it does, i used all of these * & and it does not work. Please correct me so it will return this vector.
it works like that s = "(2+23)" it will return it will return vector with tokens.
vector<string> split(string s){
vector<string>* v = new vector<string>();
int i = 0; bool b;string el = "";
while(i < s.size()){
if(isNotNumber(s.at(i))){
el = s.at(i);
v->push_back(el);
cout<<el<<" to operator"<<endl;
el = "";
i++;
}else{
while( i <s.size() && !isNotNumber(s.at(i))){//LENIWE WYLICZANIE !!!
el = el + s.at(i);
cout<<s.at(i)<<" to liczba"<<endl;
i++;
}
v->push_back(el);
el = "";
}
}
cout<<"PO while"<<endl;
for(int i = 0; i < v->size();i++){
cout<<v->at(i)<<endl;
}
return v;
}
and what about that
stack<string>* stack = new stack<string>();
type specifier before stack ;////////////////

Your function split() claims to return vector<string>, however your return value is v. What is v? It's
vector<string>* v
You are attempting to return a pointer but your function declaration states that it is not returning a pointer. Actually, there's no need for a pointer at all in your function; just use a regular vector<string> or pass in a vector by reference:
void split(string s, vector<string>& v){

The question about whether to return a pointer or a value, or pass in a reference to an existing vector was settled a long time ago: don't do any of the above. Instead, create a generic algorithm that works with iterators:
template <class InIt, class OutIt>
void tokenize(InIt b, InIt e, OutIt o) {
while (b != e) {
std::string token;
while (isdigit(*b))
token.push_back(*b++);
if (!token.empty())
*o++ = token;
while (!isdigit(*b) && b != e)
*o++ = std::string(1, *b++);
}
}
To put the result in a vector of strings, you'd do something like:
std::string i("(2+23)");
std::vector<std::string> tokens;
tokenize(i.begin(), i.end(), std::back_inserter(tokens));
Or, if you wanted to display the tokens, one per line for testing:
tokenize(i.begin(), i.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
...which will produce:
(
2
+
23
)
That fits with what I'd think of as the tokens in this input string. I should add, however, that while it works for that input, it may or may not produce what you expect for other inputs. In particular, it will treat whitespace characters as tokens, which you usually don't want (but does seem to be how the original was intended to work).

Your function signature returns a vector, but your return value is a pointer. Change the return value type to pointer to vector or return a vector by value in your function body.

This is really Blastfurnace's answer, ...
but convert the line:
vector<string>* v = new vector<string>();
into
vector<string> v;
and change all the v-> into v.
This way you fix the memory leak too. (The memory allocated by new vector<string>() is never deleted). However, vector<string> v; puts the memory on the stack and is automatically deleted when it goes out of scope.

Related

C++ "Segmentation fault" or "free(): invalid pointer" depending on input (reproducible)

This program gives a "Segmentation fault" or a "free(): invalid pointer" based on the input file used. The bug is reproducible.
This is very strange especially because free is not called.
Here is the complete program.
// Open a file, read line by line,
// and for each (loooong) line, fraction it into
// small, justified lines.
// Justification done by adding spaces to existing spaces.
#include <iostream>
#include <iterator>
#include <sstream>
#include <fstream>
#include <list>
#include <vector>
#include <math.h>
#include <random>
#include <functional>
const int pageWidth = 50; // max width for a (justified) line
typedef std::vector<std::string> WordList;
typedef std::vector<int> SpaceList;
// ========
// HELPERS
// ========
// Helper to return "cccccc...ccc"
std::string repeat (const int n, char c) {
std::string ret;
for (int i{0}; i < n; ++i) {
ret += c;
}
return ret;
}
// "Random" int between min and max (pseudo-random: reproducible)
unsigned int random_pred(std::size_t salt, unsigned int min, unsigned int max) {
unsigned int output = min + (salt % static_cast<int>(max - min + 1));
return output;
}
// alpha is greater at center
float alpha_weight(float z) { return std::max(100*(sin(z))*(sin(z)), float(1)); }
// Weight of a space, ie probability to add here blank space
int weight(int x, unsigned int l)
{
float z = 3.141 * x / l;
return alpha_weight(z);
}
// line -> vector of words
WordList splitTextIntoWords( const std::string &text )
{
WordList words;
std::istringstream in(text);
std::copy(std::istream_iterator<std::string>(in),
std::istream_iterator<std::string>(),
std::back_inserter(words));
return words;
}
// ======
// CORE
// ======
// Give each space a weight, a 'probability' to be expanded
SpaceList charge_spaces ( int l, const WordList & words, SpaceList spp)
{
SpaceList sp_weights;
std::string p{ words[0] }, h;
int wg;
for (size_t i = 0; i < words.size()-1; ++i) {
wg = weight(spp[i], l);
sp_weights.push_back(wg);
}
return sp_weights;
}
// Given weighted list of spaces positions, 'randomly' pick one
int random_sp( const SpaceList& spw, std::size_t salt ) {
std::string m;
unsigned int i{48}, total{0}; // ASCII 48 = ' '
for (const int & n : spw) {
char ch = static_cast<char>(i); // '1'; '2'; '3' ...
std::string segment = repeat(n, ch); // "11"; "2222"; "3333333333333" ....
m += segment;
total += n;
++i;
} // now, m like "11112222222222333333333333333333334444444455555", of length <total>
int mqrh = random_pred(salt, 0, total); // Get 0 <= random <= total
char iss = m[mqrh]; // Read the char at this position (for example, more likely '3' here)
int ret = static_cast<int>(iss) - 48; // Example: '3' -> 3
return ret; // Example: return 3
}
// Add spaces to a (small) line, to justify it.
// We need to expand it by <excess> spaces.
std::string justifyLine( std::string line, WordList ww, SpaceList space_positions, int excess )
{
SpaceList spwg = charge_spaces(line.size(), ww, space_positions);
SpaceList spadd{spwg.begin(), spwg.end()}; // number of spaces to add after word <i>
for (size_t k = 0; k < ww.size()-1; ++k) {
spadd[k] = 1; // By default, 1 space after each word
}
int winner; // Which space will win additional space ?
std::size_t str_hash = std::hash<std::string>{}(line) / 1000; // 'random' seed, reproducible
for (int i{0}; i < excess; ++i) { // Where to add these <excess> needed spaces ?
std::size_t salt = str_hash + 37*i;
winner = random_sp(spwg, salt);
spadd[winner] = spadd[winner] + 1; // space after <winner> word is incremented !
spwg[winner] = std::max( spwg[winner] / 10, 1); // Weight of winner is decreased
}
// Build up the justified line
std::string justified;
for (size_t j = 0; j < ww.size()-1; ++j) {
justified.append(ww[j]); // Add next word
justified.append(spadd[j], ' '); // Add few spaces
}
justified.append(ww.back()); // Add last word
std::cout << justified << std::endl;
return justified;
}
// Fraction a long line in several justified small lines
void justifyText( const std::string& text )
{
WordList words = splitTextIntoWords(text);
std::string line;
WordList ww;
SpaceList space_positions;
int position{0};
int nwords_in_line{0};
for (const std::string& word : words) {
size_t s = word.size();
if (line.size() + s + 1 <= pageWidth) { // next word fit into the line.
if (!line.empty()) {
line.append(" ");
space_positions.push_back(position++);
}
line.append(word);
nwords_in_line++;
ww.push_back(word); // append this word to the list
position += s;
} else { // build a justified small line from the words added up
justifyLine(line, ww, space_positions, pageWidth - position);
line.clear(); // Cleaning for next chunk
ww.clear();
space_positions.clear();
line = word;
position = s;
nwords_in_line = 1;
ww.push_back(word); // don't forget the last word (that overflowed)
}
}
std::cout << line << std::endl; // Remaining of the long line
}
// =====
// main
// =====
int main () {
std::string line;
std::ifstream myfile ("candle.txt");
if (myfile.is_open())
{
while ( getline(myfile,line) )
{
justifyText(line);
}
myfile.close();
}
else std::cerr << "Unable to open file";
return 0;
}
File "candle.txt" is an ASCII text file, here is a copy.
The whole file gives free(): invalid pointer, always at same position -- see below (1)
If cutting between the two markups in the PREFACE (deleting the chunk between the two CUT HEREmarks), program gives a Segmentation fault.
Running with Valgrind gives this (very strange because repeat function does not seem problematic)
Thread 1: status = VgTs_Runnable (lwpid 4487)
==4487== at 0x4838DEF: operator new(unsigned long) (vg_replace_malloc.c:342)
==4487== by 0x4990859: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_mutate(unsigned long, unsigned long, char const*, unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==4487== by 0x4990F34: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::operator+=(char) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==4487== by 0x10A406: repeat[abi:cxx11](int, char) (in /home/fig/Documents/cpp/cil)
==4487== by 0x10A8DD: random_sp(std::vector<int, std::allocator<int> > const&, unsigned long) (in /home/fig/Documents/cpp/cil)
==4487== by 0x10AB34: justifyLine(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::vector<int, std::allocator<int> >, int) (in /home/fig/Documents/cpp/cil)
==4487== by 0x10AF71: justifyText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (in /home/fig/Documents/cpp/cil)
==4487== by 0x10B195: main (in /home/xxx/Documents/cpp/cil)
Any idea welcome
(1) End of output:
We have several tests for oxygen besides the mere
burning of bodies. You have seen a candle burnt in
oxygen, or in the air; you have seen phosphorus
burnt in the air, or in oxygen; and you have seen
iron-filings burnt in oxygen. But we have other
tests besides these, and I am about to refer to
one or two of them for he purpose of carrying
The crash occurs as a result of a consequence from an earlier bug. There's nothing (directly) wrong with any of the code in your backtrace. Although the bug effectively was traced to one of the function the actual crash was triggered from an earlier invocation of the same code, and the actual bug would be triggered later, after returning from the point of the crash.
One of my stock comments starts with: "Just because this is where the program crashes or reports an error doesn't mean this is where the problem is. C++ does not work this way." This is followed by explanation that a minimal reproduce example must be provided, which you mostly did. This allowed the problem to be reproduced trivially. The first instance of undefined behavior occured elsewhere, on line 109:
spadd[winner] = spadd[winner] + 1;
valgrind has a very useful option: --vgdb-error=1. This stops execution immediately when valgrind detects memory corruption, on this line, in this case. valgrind then gives instruction for attaching to the current process, with gdb. Doing so immediately led to the observation that this value of winner was -48. Modifying spadd[-48] will only result in tears.
At this point it wasn't too difficult to backtrack to line 91, where -48 came from, which led to the actual bug, an off by 1:
int mqrh = random_pred(salt, 0, total);
total here was always the same as m.size(), and this duplicate logic resulted in the bug, since this parameter should be the last value in the range for random_pred, and not one past it. The expected results here is to pick a random character from m, so the valid range is 0 to m.size()-1. If it wasn't for the duplicated logic, being mindful of how random_pred()'s parameters must be defined, the last parameter would've naturally be m.size()-1. But the duplicated logic resulted in an indirect reference to the underlying value, total, and this detail was forgot.
Another contributing factor to common kinds of bugs is going against the natural flow of how C++ defines ranges and sequences: not by the minimum and the maximum value, but the minimum and one past the maximum value. std::string::size(), std::vector::size, et. al., is one past the last valid index of the underlying container, and not the last valid index of the container. Similarly, end(), the ending iterator is not the iterator for the last value in the sequence, but the iterator to the next, non-existent value in the sequence, "one past it".
If random_pred was designed in harmony with the rest of the C++ library, its formula would simply involve min + salt % (max-min) instead of min + salt % (max-min+1). Then it wouldn't matter if its third parameter was total or m.size(), it would've naturally worked either way.

Why doesn't the push_back function accept the value/parameter?

I am trying to store some specific characters of a string in a vector. When I want to push back the characters though, there is a problem with the value and I do not know why. Going over the cpp reference page didn't help me unfortunately. Could someone please help?
Here is the code:
int main()
{
std::string str1 = "xxxGGxx$xxxGxTxGx";
std::vector<std::string> vec;
std::vector<std::string>::iterator it;
for(auto &ch:str1)
{
if(ch == 'G' || ch == '$' || ch == 'T')
{
vec.push_back(ch); //Problem: ch not accepted
}
}
for(it = vec.begin(); it!=vec.end(); it++)
{
std::cout << *it;
}
}
Vector needs to be of type char, not string.
The reason vec.push_back(ch); does not work is because vec is a vector of strings and not a vector of char. push_back() only accepts the type stored inside the vector, in this case string. A vector<int> cannot push_back a string because a string can't be implicitly converted to an int. It has to somehow be converted to an int first then pushed back. Since a char can't be implicitly converted into a string the compiler gets confused and thinks its impossible.
There are two simple alternatives:
Instead of using vector<string> use vector<char> that way push_back() will append characters.
vector<char> vec;
char ch = 'a';
vec.push_back(ch); // both these work
vec.push_back('b'); // both these work
Or, Convert your char into a string and then call push_back onto your string.
string str(1, ch); // Creates a string containing 1 character equal to ch
vec.push_back(str); // Push back our string

problems about map STL

void huffmanDecode(string str){
string temp;
map<string, char>::iterator it;
//for(auto iter=myMap.begin();iter!=myMap.end();++iter)
//cout<<iter->first<<" "<<iter->second<<" "<<endl;
for (unsigned int i = 0; i < str.size(); i++)
{
temp += str[i];
it = myMap.find(temp);
if (it == myMap.end())
continue;
else
{
cout<<it->first<<" ";//crashed here, "Thread 1:EXC_BAD_ACCESS(code=1,address=0x0)
//cout << it->second << " ";
temp = nullptr;
}
}
}
I am trying solving the huffmandecode problem by map ,but it crashed ~~~
std::string::operator= has an overload that takes a const char*. This is the overload that is used when you say
temp = nullptr;
Now, a requirement is that the const char* point to a null-terminated string. Thus it cannot be the null pointer. You are not allowed to pass a null pointer, and an implementation is allowed to raise an exception in this case. In any case, attempting to use such a string would result in undefined behaviour. There is an analogous situation with the std::string constructor.
If your intention was to re-set temp to be an empty string, you have a few options:
temp = "";
temp.clear();
temp = std::string();
You have defined temp as a std::string, not as a pointer.
So setting it to nullptr is wrong!
If you want to clear its contents, which I assume you actually want to, try this:
temp.clear();

Invalid argument in c++ with Qt integration

I'm trying to write a Qt program in eclipse with C++, but I can't get past an error:
void MyTests::populateFirstList(){
Question* q = new Question;
q = this->ctr->getCurrent();
string s = this->ctr->toString(q);
}
Question is a type defined by me, and the line with toString(q) returns an error saying invalid arguments.
The funcion toString():
string Controller::toString(Question* q){
string s="";
string text = q->getText();
char c;
string::iterator it;
for (it= text.begin(); it != text.end(); it++)
{
if ((*it) == ' ') {
s+="\n";
}
else {
s+=it;
}
}
return s;
}
And just to be safe, the function getCurrent():
Question* Controller::getCurrent(){
return this->question;
}
I don't understand why this happens, because the function toString() should take a pointer to a Question, and q is one. I'm not even sure if the error is caused within these functions or somewhere deeper. Thanks for any help.
The error message is:
invalid arguments ' Candidates are:
std::basic_string < char,std::char_traits < char >, std::allocator < char > >
toString(Question *) '
Perform : Run QMake
then build
:)
Eventually your error comes from line
} else s+=it;
which eventually should be
} else s+=*it;
This will probably not solve the question, but i do not see any QT in here. Why not make use of QT-Objects when writing a Qt-App?
void MyTests::populateFirstList(){
Question* q = this->ctr->getCurrent();
QString s = this->ctr->toString(q);
}
QString Controller::toString(Question* q){
QString s;
QString text = q->getText();
s = text.replace(" ","\\n");
return s;
}
Question* Controller::getCurrent(){
return this->question;
}

Permutations of letters and numbers in a phone number

For my computer science class, we need to write a program (in C++) that takes an input of characters and outputs the possible permutations of it according to the dial pad on a phone, leaving non-digit characters in place.
For example, inputing 2 outputs 2, A, B, C. Inputing 23 outputs 23, A3, B3, C3, 2D, 2E, 2F, AD, AE, AF, BD, BE, BF, etc...
The application provided for this program is finding permutations of "vanity" phone numbers for a given phone number.
Currently, the program I have written doesn't even compile, and I'm afraid the algorithm I'm using is incorrect:
#include <iostream>
#include <multimap.h>
#include <vector>
using namespace std;
// Prototypes
void initLetterMap(multimap<char,char> &lmap);
void showPermutations(const vector<string> &perms);
vector<string> getPermutations(const string &phoneNumber,const multimap<char,char> &lmap);
vector<char> getLetters(char digit, const multimap<char,char> &lmap);
// Declarations
void initLetterMap(multimap<char,char> &lmap) {
lmap.insert(pair<char,char>('1','1'));
lmap.insert(pair<char,char>('2','2'));
lmap.insert(pair<char,char>('2','A'));
lmap.insert(pair<char,char>('2','B'));
lmap.insert(pair<char,char>('2','C'));
lmap.insert(pair<char,char>('3','3'));
lmap.insert(pair<char,char>('3','D'));
lmap.insert(pair<char,char>('3','E'));
lmap.insert(pair<char,char>('3','F'));
// ...
}
vector<char> getLetters(char digit, const multimap<char,char> &lmap) {
multimap<char,char>::iterator it;
pair<multimap<char,char>::iterator,multimap<char,char>::iterator> range;
vector<char> result;
if (isdigit(digit)) {
range = lmap.equal_range(digit);
for (it=range.first;it!=range.second;++it) {
result.push_back((*it).second);
}
} else {
result.insert(result.end(),digit);
}
return result;
}
void showPermutations(vector<string> &perms) {
vector<string>::iterator it;
for (it = perms.begin(); it != perms.end(); it++) {
cout << *it << endl;
}
}
vector<string> getPermutations(const string &phoneNumber,const multimap<char,char> &lmap) {
vector<string> results;
string number = phoneNumber;
vector<char>::iterator vcit;
vector<char> letters;
unsigned int i;
for (i=0;i<phoneNumber.length();i++) {
letters = getLetters(number[i],lmap);
for (vcit=letters.begin();vcit!=letters.end();vcit++) {
number[i] = *vcit;
results.push_back(number);
}
}
return results;
}
int main() {
multimap<char,char> lmap;
initLetterMap(lmap);
string input;
cout << "Enter a phone number to get all possible vanity numbers" << endl;
cout << "> "; getline(cin,input);
showPermutations(getPermutations(input,lmap));
return 0;
}
I get a whole slew of build issues when I try to build this, and am not sure how to resolve most of them:
In file included from /usr/include/c++/4.0.0/backward/multimap.h:59,
from phone02.cpp:18:
/usr/include/c++/4.0.0/backward/backward_warning.h:32:2: warning: #warning This file includes at least one deprecated or antiquated header. Please consider using one of the 32 headers found in section 17.4.1.2 of the C++ standard. Examples include substituting the <X> header for the <X.h> header for C++ includes, or <iostream> instead of the deprecated header <iostream.h>. To disable this warning use -Wno-deprecated.
/usr/include/c++/4.0.0/bits/stl_pair.h: In constructor 'std::pair<_T1, _T2>::pair(const std::pair<_U1, _U2>&) [with _U1 = std::_Rb_tree_const_iterator<std::pair<const char, char> >, _U2 = std::_Rb_tree_const_iterator<std::pair<const char, char> >, _T1 = std::_Rb_tree_iterator<std::pair<const char, char> >, _T2 = std::_Rb_tree_iterator<std::pair<const char, char> >]':
phone02.cpp:75: instantiated from here
/usr/include/c++/4.0.0/bits/stl_pair.h:90: error: no matching function for call to 'std::_Rb_tree_iterator<std::pair<const char, char> >::_Rb_tree_iterator(const std::_Rb_tree_const_iterator<std::pair<const char, char> >&)'
/usr/include/c++/4.0.0/bits/stl_tree.h:167: note: candidates are: std::_Rb_tree_iterator<_Tp>::_Rb_tree_iterator(std::_Rb_tree_node<_Tp>*) [with _Tp = std::pair<const char, char>]
/usr/include/c++/4.0.0/bits/stl_tree.h:164: note: std::_Rb_tree_iterator<_Tp>::_Rb_tree_iterator() [with _Tp = std::pair<const char, char>]
/usr/include/c++/4.0.0/bits/stl_tree.h:152: note: std::_Rb_tree_iterator<std::pair<const char, char> >::_Rb_tree_iterator(const std::_Rb_tree_iterator<std::pair<const char, char> >&)
/usr/include/c++/4.0.0/bits/stl_pair.h:90: error: no matching function for call to 'std::_Rb_tree_iterator<std::pair<const char, char> >::_Rb_tree_iterator(const std::_Rb_tree_const_iterator<std::pair<const char, char> >&)'
/usr/include/c++/4.0.0/bits/stl_tree.h:167: note: candidates are: std::_Rb_tree_iterator<_Tp>::_Rb_tree_iterator(std::_Rb_tree_node<_Tp>*) [with _Tp = std::pair<const char, char>]
/usr/include/c++/4.0.0/bits/stl_tree.h:164: note: std::_Rb_tree_iterator<_Tp>::_Rb_tree_iterator() [with _Tp = std::pair<const char, char>]
/usr/include/c++/4.0.0/bits/stl_tree.h:152: note: std::_Rb_tree_iterator<std::pair<const char, char> >::_Rb_tree_iterator(const std::_Rb_tree_iterator<std::pair<const char, char> >&)
make: *** [phone02.o] Error 1
The line numbers are a bit off, but the important ones that I can see are the two about no matching function for call to 'std::_Rb_tree_iterator<std::pair<const char, char> >::_Rb_tree_iterator(const std::_Rb_tree_const_iterator<std::pair<const char, char> >&)'
Besides the errors, I also believe I am heading in the wrong direction with my algorithm.
So I have 2 questions here:
Why am I getting these build errors, and how do I fix them?
How would you suggest going about solving this problem? Am I on the right track or no?
For question #2, I would prefer to not get solutions, just advice or pointers in the right direction.
Thanks!
PS: I am building this on Mac OS X 10.5.8 with gcc, using QtCreator 1.2.1
UPDATE:
I have successfully compiled a solution program. I will post the source code to those who are curious.
#include <iostream>
#include <map>
#include <vector>
#include <string>
using namespace std;
void initLetterMap(map<char,string> &lmap);
vector<string> getMapped(const string &phoneNumber, map<char,string> &lmap);
vector<string> getPermutations(vector<string> number);
unsigned long int countPermutations(vector<string> number);
void initLetterMap(map<char,string> &lmap) {
lmap['0'] = "0";
lmap['1'] = "1";
lmap['2'] = "2ABC";
lmap['3'] = "3DEF";
lmap['4'] = "4GHI";
lmap['5'] = "5JKL";
lmap['6'] = "6MNO";
lmap['7'] = "7PQRS";
lmap['8'] = "8TUV";
lmap['9'] = "9WXYZ";
}
unsigned long int countPermutations(vector<string> number) {
long int fold = 1;
int vals = 0;
vector<string>::iterator it;
for (it=number.begin();it!=number.end();it++) {
vals = (*it).length();
fold *= vals;
}
return fold;
}
vector<string> getMapped(const string &phoneNumber, map<char,string> &lmap) {
unsigned int i;
vector<string> out;
char digit;
string temp;
for (i=0;i<phoneNumber.length();i++) {
digit = phoneNumber.at(i);
if (isdigit(digit)) {
out.push_back(lmap[digit]);
} else {
temp = string(1,digit);
out.push_back(temp);
}
}
return out;
}
vector<string> getPermutations(vector<string> number) {
vector<string> results;
unsigned long int i,j,k;
unsigned long int perms = countPermutations(number);
vector<string>::reverse_iterator numit;
string temp,temp2;
vector<int> state = vector<int>(number.size(), 0);
vector<int>::reverse_iterator stateit;
for (i=0;i<perms;i++) {
j=i;
temp = "";
for (stateit=state.rbegin(), numit=number.rbegin();stateit!=state.rend();stateit++, numit++) {
*stateit = j % (*numit).length();
j /= (*numit).length();
temp.insert(temp.begin(),(*numit)[*stateit]);
}
results.push_back(temp);
}
return results;
}
int main() {
map<char,string> lettermap;
initLetterMap(lettermap);
string input;
cout << "> "; getline(cin,input);
vector<string> perms = getPermutations(getMapped(input,lettermap));
vector<string>::iterator it;
for (it=perms.begin();it!=perms.end();it++) {
cout << *it << endl;
}
}
The code is probably more complicated than it has to be, but my goal was to just get it to work. It seems to run fairly quickly for 10 digit phone numbers, so I guess it's not too bad.
Thanks to Jacob and ShreevatsaR for getting me pointed in the right direction!
How about the following:
#include <cstddef>
#include <iostream>
#include <iterator>
#include <string>
#include <algorithm>
template <typename Iterator>
bool next_combination(const Iterator first, Iterator k, const Iterator last);
int main()
{
std::string phone_number = "23";
std::string number[] = {
"0", "1", "2abc", "3def", "4ghi",
"5jkl","6mno", "7pqrs", "8tuv","9wxyz"
};
std::string tmp_set;
std::string set;
for(std::size_t i = 0; i < phone_number.size(); ++i)
{
tmp_set += number[static_cast<std::size_t>(phone_number[i] - '0')];
}
std::sort(tmp_set.begin(),tmp_set.end());
std::unique_copy(tmp_set.begin(),
tmp_set.end(),
std::back_inserter(set));
std::string current_set;
current_set.reserve(phone_number.size());
do
{
std::copy(set.begin(),
set.begin() + phone_number.size(),
std::back_inserter(current_set));
do
{
std::cout << current_set << std::endl;
}
while (std::next_permutation(current_set.begin(),current_set.end()));
current_set.clear();
}
while(next_combination(set.begin(),
set.begin() + phone_number.size(),
set.end()));
return 0;
}
template <typename Iterator>
inline bool next_combination(const Iterator first, Iterator k, const Iterator last)
{
/* Credits: Thomas Draper */
if ((first == last) || (first == k) || (last == k))
return false;
Iterator itr1 = first;
Iterator itr2 = last;
++itr1;
if (last == itr1)
return false;
itr1 = last;
--itr1;
itr1 = k;
--itr2;
while (first != itr1)
{
if (*--itr1 < *itr2)
{
Iterator j = k;
while (!(*itr1 < *j)) ++j;
std::iter_swap(itr1,j);
++itr1;
++j;
itr2 = k;
std::rotate(itr1,j,last);
while (last != j)
{
++j;
++itr2;
}
std::rotate(k,itr2,last);
return true;
}
}
std::rotate(first,k,last);
return false;
}
Well if you don't want a solution, I would implement it like this:
Use a vector V with each element drawn from a string. E.g. if it's 23, then your vector V, would have two vectors each containing 2ABC and 3DEF.
Iterate each digit like a counter through the associated string. Move right-to-left and when each digit overflows increment the one to the left, and reset, etc.
Display the counter at every iteration to obtain your "number".
Hint to compile errors:
there is no file called <multimap.h> in STL. it should be <map>
The problem is with getLetters function. multimap lmap has been passed by const reference. Hence, equal_range API on lmap would return pair of const_iterator not just iterator.
Below code demonstrate how to do it in simple steps(Recursively):
1.) Create vector of strings, and push strings which represents "possible characters resulting from that keypress" (0-key to 9-key).
2.) Since each keypress will serve to add ONLY one char to the resultant string, append only one character at a time and call the function recursively for the next keypress. Do it for each possible char at that keypress.
Demo:
void getCombination(vector<string> &combinations, string current, const string &A, int idx, const vector<string> &keyset){
if(idx >= A.size()) {
combinations.push_back(current);
return;
}
int key = (A[idx] - '0');
int len = keyset[key].length();
for( int i = 0; i < len; i++ ){
current.push_back(keyset[key][i]); //pick at once one char corresp. to that keypress
getCombination(combinations, current, A, idx+1, keyset);
current.pop_back();
}
}
vector<string> letterCombinations(string A) {
vector<string> combinations;
vector<string> keyset{"0", "1", "2abc", "3def", "4ghi", "5jkl", "6mno", "7pqrs", "8tuv", "9wxyz"};
getCombination(combinations, std::string({}), A, 0, keyset);
return combinations;
}
int main() {
vector<string> combinations = letterCombinations("23");
for(string word : combinations){
cout << word << ", ";
}
return 0;
}
Gives Output :
23, 2d, 2e, 2f, a3, ad, ae, af, b3, bd, be, bf, c3, cd, ce, cf,