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,
Related
I am trying to debug this code from my supervisor and I'm new to C++.
I found a few similar no matching function for call to questions, which gave me ideas what the problem might be but was not able to solve it.
I have listed my thoughts below the error message and relevant function.
Error message:
In function ‘int main(int, char**)’:
distanceMatrixToSageGraph.c:104:43: error: no matching function for call to
‘std::vector<std::vector<int>>::push_back(std::vector<std::__cxx11::basic_string<char> >&)’
the_entries.push_back( row_as_vector );
Relevant function:
int main(int argc, char** threshold_and_distanceMatrixfilename)
{
if (argc < 2 || argc > 2)
{
std::cerr << "Usage: ./distanceMatrixToSageGraph.o <threshold>
<distanceMatrix_file_calculated_fromDGEsingleCell_data>" << std::endl;
return -1;
}
string distanceMatrixfilename = threshold_and_distanceMatrixfilename[2];
int threshold = std::stoi(threshold_and_distanceMatrixfilename[1]);
std::ifstream distanceMatrixFile(distanceMatrixfilename);
if (!distanceMatrixFile)
{
std::cerr << "Error opening distanceMatrix file: " << distanceMatrixfilename << std::endl;
return -1;
}
string row;
std::getline(distanceMatrixFile, row); // discard the first row, which specifies the format of the file.
vector<vector<int>> the_entries;
while (std::getline(distanceMatrixFile, row))
{
std::stringstream row_as_stringstream(row);
int i; i = 0;
vector<string> row_as_vector;
while (row_as_stringstream.good())
{
string substr;
getline(row_as_stringstream, substr, ',');
row_as_vector.push_back(substr);
};
the_entries.push_back(row_as_vector); //LINE 104
};
}
Ideas:
It would be great if someone could explain to me: std::vector<std::vector<int>>::push_back(std::vector<std::__cxx11::basic_string<char>>&)
I understand that std::vector<std::vector<int>> is a matrix and
that push_back adds an element at the end of the vector.
I suspect that we're trying to read in the wrong type in
the_entries.push-back(row_as_vector).
The original code took a csv file as input, containing integers
and strings. Now we're reading in a txt file having the shape:
TEXT
0; INT; INT; INT; INT; ...
0; INT; INT; INT; INT; ...
18 more lines of the above numbers and semicolons
In the above, we remove the first row in line 89.
Is it possible the code has to be changed much more to be able to
read this txt file instead of the csv file?
Rest of error message:
In file included from /usr/include/c++/6/vector:64:0,
from distanceMatrixToSageGraph.c:13:
/usr/include/c++/6/bits/stl_vector.h:914:7: note: candidate: void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::vector<int>; _Alloc = std::allocator<std::vector<int> >; std::vector<_Tp, _Alloc>::value_type = std::vector<int>]
push_back(const value_type& __x)
^~~~~~~~~
/usr/include/c++/6/bits/stl_vector.h:914:7: note: no known conversion for argument 1 from ‘std::vector<std::__cxx11::basic_string<char> >’ to ‘const value_type& {aka const std::vector<int>&}’
/usr/include/c++/6/bits/stl_vector.h:932:7: note: candidate: void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = std::vector<int>; _Alloc = std::allocator<std::vector<int> >; std::vector<_Tp, _Alloc>::value_type = std::vector<int>]
push_back(value_type&& __x)
^~~~~~~~~
/usr/include/c++/6/bits/stl_vector.h:932:7: note: no known conversion for argument 1 from ‘std::vector<std::__cxx11::basic_string<char> >’ to ‘std::vector<std::vector<int> >::value_type&& {aka std::vector<int>&&}’
Whole code:
// Convert distanceMatrix tables of protein interactions to SAGE graph.
///////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <fstream>
#include <sstream>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <list>
#include <vector>
#include <tuple>
#include <algorithm>
using namespace std;
void writeGraphInSageFormat(string name, std::vector<std::vector<int>> TheEdges)
{
//////////////////////////////////////////////////////////////////////////////////////
// Write out the edges in SAGE format.
///////////////////////////////////////////////////////////////////////////////////////
int edgeNumber = TheEdges.size();
ofstream d1sageFile(name, ios::out);
d1sageFile << "g = Graph([" << endl;
for (int n = 0; n < edgeNumber; n++) {
d1sageFile << "(" << TheEdges[n][0] + 1 << "," << TheEdges[n][1] + 1 << ")," << endl;
}
d1sageFile << "])" << endl;
d1sageFile << "g.show()" << endl;
d1sageFile.close();
std::cout << "SAGE graph written into the file " << name << std::endl;
}
std::vector<std::vector<int>> ConvertEntriesMatrixToEdges(vector<vector<int>> the_entries, int threshold)
{
////////////////////////////////////////////////////////////////////////////////////////////
// Construct the edge-vertex incidence matrix (d_1) from the distanceMatrix entries matrix:
////////////////////////////////////////////////////////////////////////////////////////////
std::vector<std::string> proteinNames;
std::vector<std::vector<int>> TheEdges;
std::cout << "Registering only edges shorter than " << threshold << "." << std::endl;
int thisDistance;
for (int i = 0; i < the_entries.size(); i++)
{
for (int j = i + 1; j < the_entries.size(); j++)
{
// we could use the_entries.size() instead of the_entries.at(i).size(), because this is a square matrix.
thisDistance = the_entries.at(i).at(j);
if (thisDistance < threshold)
{
std::vector<int> CurrentEdge(2);
CurrentEdge[0] = i;
CurrentEdge[1] = j;
TheEdges.push_back(CurrentEdge);
};
};
};
return TheEdges;
}
///////////////////////////////////////////
// Main Program: Extract edges from a distanceMatrix file.
///////////////////////////////////////////
int main(int argc, char** threshold_and_distanceMatrixfilename)
{
if (argc < 2 || argc > 2)
{
std::cerr << "Usage: ./distanceMatrixToSageGraph.o <threshold> <distanceMatrix_file_calculated_fromDGEsingleCell_data>" << std::endl;
return -1;
}
string distanceMatrixfilename = threshold_and_distanceMatrixfilename[2];
int threshold = std::stoi(threshold_and_distanceMatrixfilename[1]);
std::ifstream distanceMatrixFile(distanceMatrixfilename);
if (!distanceMatrixFile) {
std::cerr << "Error opening distanceMatrix file: " << distanceMatrixfilename << std::endl;
return -1;
}
string row; //LINE 88
std::getline(distanceMatrixFile, row); // discard the first row, which specifies the format of the file.
vector<vector<int>> the_entries;
while (std::getline(distanceMatrixFile, row))
{
std::stringstream row_as_stringstream(row);
int i; i = 0;
vector<string> row_as_vector;
while (row_as_stringstream.good())
{
string substr;
getline(row_as_stringstream, substr, ',');
row_as_vector.push_back(substr);
};
the_entries.push_back(row_as_vector); //LINE 104
};
////////////////////////////////////////////////////////////
// Now we assemble the entries to an edges matrix, and write it into a Sage file:
////////////////////////////////////////////////////////////
std::vector<std::vector<int>> TheEdges = ConvertEntriesMatrixToEdges(the_entries, threshold);
char outputFilename[60]; strcpy(outputFilename, distanceMatrixfilename.c_str()); strcat(outputFilename, "AtThreshold"); string thrshld = std::to_string(threshold); strcat(outputFilename, thrshld.c_str()); strcat(outputFilename, ".txt");
writeGraphInSageFormat(outputFilename, TheEdges);
return 0;
}
I suspect that we're trying to read in the wrong type in
the_entries.push-back(row_as_vector)
error: no matching function for call to
‘std::vector<std::vector<int>>::push_back(std::vector<std::__cxx11::basic_string<char> >&)’
the_entries.push_back( row_as_vector );
Yes, your right about this. The error message says that you are trying to push back a vector of strings to a vector of vector of integers. They are entirely different types and hence not possible.
You probably want to either change the type of the_entries to
std::vector<std::vector<std::string>> the_entries;
// ^^^^^^^^^^^^
or
convert the strings to integers using std::stoi while pushing back to the std::vector<int> row_as_vector.
std::vector<int> row_as_vector;
while(row_as_stringstream.good())
{
// ......
row_as_vector.push_back(std::stoi(substr));
// ^^^^^^^^^^^^^^^^^^^
};
the_entries.push_back( row_as_vector );
Is it possible the code has to be changed much more to be able to read
this txt file instead of the csv file?
If the contents of those two are same, you don't need to make much difference in your code. However, ; should be parsed out before calling std::stoi to them. Because it may through invalid_argument exception for the bad argument.
A few suggestions:
Consider not to practice with using namespace std;. See this post
for more:
Why is "using namespace std;" considered bad practice?
When you pass the non-primitive types(i.e. std::string,
std::vector.. etc) to the function, consider the case, if the
parameters are read-only or not. If the data should be modified
pass-by-ref and if read-only(no modification should have been made inside the function) pass them by const-ref. Read more:
How to pass objects to functions in C++?
I want to write c++ code which will separate all elements which have same length as input value
#include <iostream>
#include <vector>
using namespace std;
string database[] = {"green", "stupid", "boy", "girl", "forest", "mobile", "morning", "love", "keyboard", "incredible"};
string input;
string count(string input, string database[]){
string lengthFilter[] = {};
int inputLength = input.length();
for (int i = 0; i < database->length(); ++i){
if (database[i].length() == inputLength)
{
lengthFilter[lengthFilter->length()] = database[i];
}
}
return (lengthFilter);
}
int main()
{
cin >> input;
cout << count(input, database);
return 0;
}
but it gives error
main.cpp: In function 'std::__cxx11::string count(std::__cxx11::string, std::__cxx11::string*)':
main.cpp:16:22: error: could not convert 'lengthFilter' from 'std::__cxx11::string [0] {aka std::__cxx11::basic_string<char> [0]}' to 'std::__cxx11::string {aka std::__cxx11::basic_string<char>}'
return (lengthFilter);
^
exit status 1
I can't find my mistake, please, help me.
You stepped into the shortcomings of built-in C++ arrays and the [] syntax. This:
[]
Does not always mean what you think it means. Its meaning varies by context. In declarations:
string database[] = // ...
It means you're declaring a built-in array. However, in function arguments:
string count(string input, string database[])
it's a pointer. The above line is equivalent to:
string count(string input, string* database)
Also note that this:
string lengthFilter[] = {};
Declares a built-in array of zero size. I don't think this is what you want, since built-in arrays are fixed size. They don't resize.
So what you should be doing here, is use std::vector instead:
#include <iostream>
#include <vector>
using namespace std;
vector<string> database = {
"green", "stupid", "boy", "girl", "forest", "mobile", "morning", "love",
"keyboard", "incredible"
};
string input;
vector<string> count(string input, const vector<string>& database)
{
vector<string> lengthFilter;
auto inputLength = input.length();
for (const auto& i : database) {
if (i.length() == inputLength) {
lengthFilter.push_back(i);
}
}
return lengthFilter;
}
int main()
{
cin >> input;
for (const auto& i : count(input, database)) {
cout << i << '\n';
}
return 0;
}
I have a directory containing files {"good_6", good_7", "good_8"...,"good_660"}, after reading it using readdir and storing in a vector I get {"good_10", "good_100", "good_101", "good_102"...}.
What I want to do is to keep the file names as {"good_6", good_7", "good_8"...,"good_660"} in the vector and then replacing first name with 1, second with 2 and so on... such that good_6 will be 1, good_7 will be 2 and so on. but now good_10 corresponds to 1 and good_100 to 2 and so on.
I tried std::sort on vector but the values are already sorted, just not in a way that I desire (based on integer after _). Even if I just get the last integer and sort on that, it will still be sorted as 1, 100, 101...
Any help would be appreciated. Thanks.
You can use a custom function that compares strings with a special case for digits:
#include <ctype.h>
int natural_string_cmp(const char *sa, const char *sb) {
for (;;) {
int a = (unsigned char)*sa++;
int b = (unsigned char)*sb++;
/* simplistic version with overflow issues */
if (isdigit(a) && isdigit(b)) {
const char *sa1 = sa - 1;
const char *sb1 = sb - 1;
unsigned long na = strtoul(sa1, (char **)&sa, 10);
unsigned long nb = strtoul(sb1, (char **)&sb, 10);
if (na == nb) {
if ((sa - sa1) == (sb - sb1)) {
/* XXX should check for '.' */
continue;
} else {
/* Perform regular strcmp to handle 0 :: 00 */
return strcmp(sa1, sb1);
}
} else {
return (na < nb) ? -1 : +1;
}
} else {
if (a == b) {
if (a != '\0')
continue;
else
return 0;
} else {
return (a < b) ? -1 : 1;
}
}
}
}
Depending on your sorting algorithm, you may need to wrap it with an extra level of indirection:
int natural_string_cmp_ind(const void *p1, const void *p2) {
return natural_string_cmp(*(const char * const *)p1, *(const char * const *)p2);
}
char *array[size];
... // array is initialized with filenames
qsort(array, size, sizeof(*array), natural_string_cmp_ind);
I think you can play around with your data structure. For example instead of vector<string>, you can convert your data to vector< pair<int, string> >. Then {"good_6", "good_7", "good_8"...,"good_660"} should be {(6, "good"), (7, "good"), (7, "good")..., (660, "good")}. In the end, you convert it back and do whatever you want.
Another way is just to define your own comparator to do the exact comparison as what you want.
You can use string::replace to replace string "good_" with empty string, and use stoi to convert the rest of the integral part of the string. Lets say the value obtained is x.
Create std::map and populate it in this way myMap[x] = vec_element.
Then you can traverse from m.begin() till m.end() to find sorted order.
Code:
myMap[ stoi( vec[i].replace(0,5,"") )] = vec[i];
for( MapType::iterator it = myMap.begin(); it != myMap.end(); ++it ) {
sortedVec.push_back( it->second );
If I understand your question, you're just having trouble with the sorting and not how you plan to change the names after you sort.
Something like this might work for you:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <tuple>
#include <string.h>
int main()
{
std::vector<std::string> v;
char buffer[64] = {};
for (size_t i = 1; i < 10; ++i)
{
sprintf(buffer, "good_%d", i * 3);
v.push_back(buffer);
sprintf(buffer, "bad_%d", i * 2);
v.push_back(buffer);
}
std::random_shuffle(v.begin(), v.end());
for (const auto& s : v)
{
std::cout << s << "\n";
}
std::sort(v.begin(), v.end(),
[](const std::string& lhs, const std::string& rhs)
{
//This assumes a lot about the contents of the strings
//and has no error checking just to keep things short.
size_t l_pos = lhs.find('_');
size_t r_pos = rhs.find('_');
std::string l_str = lhs.substr(0, l_pos);
std::string r_str = rhs.substr(0, r_pos);
int l_num = std::stoi(lhs.substr(l_pos + 1));
int r_num = std::stoi(rhs.substr(r_pos + 1));
return std::tie(l_str, l_num) < std::tie(r_str, r_num);
});
std::cout << "-----\n";
for (const auto& s : v)
{
std::cout << s << "\n";
}
return 0;
}
Managed to do it with the following compare function:
bool numericStringComapre(const std::string& s1, const std::string& s2)
{
size_t foundUnderScore = s1.find_last_of("_");
size_t foundDot = s1.find_last_of(".");
string s11 = s1.substr(foundUnderScore+1, foundDot - foundUnderScore - 1);
foundUnderScore = s2.find_last_of("_");
foundDot = s2.find_last_of(".");
string s22 = s2.substr(foundUnderScore+1, foundDot-foundUnderScore - 1);
int i1 = stoi(s11);
int i2 = stoi(s22);
if (i1 < i2) return true;
return false;
}
full file name was good_0.png, hence that find_last_of(".").
After compiling my hash_multimap I get this large one paragraph error for my
In member function ‘size_t __gnu_cxx::hashtable<
I have never seen this huge error and I'm actually not sure what to do to fix this error due to it's abnormal size.
Any suggestions why line:
p = map1.equal_range(searchKey);
is causing this abnormaly long error? FYI- I cut the whole error and removed the middle part because once I pasted it here it was like a page long O.o
ERROR
/usr/include/c++/4.3/backward/hashtable.h: In member function ‘size_t
__gnu_cxx::hashtable<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey,
_Alloc>::_M_bkt_num_key(const _Key&, size_t) const [with _Val =
std::pair<const std::basic_string<char, std::char_traits<char>,
std::allocator<char> >, Map2*>, _Key = std::basic_string<char
...
std::char_traits<char>, std::allocator<char> > >, _Alloc =
std::allocator<Map2*>]’
hash_map2.cpp:55: instantiated from here
/usr/include/c++/4.3/backward/hashtable.h:595: error: no match for call to
‘(const __gnu_cxx::hash<std::basic_string<char, std::char_traits<char>,
std::allocator<char> > >) (const std::basic_string<char,
std::char_traits<char>, std::allocator<char> >&)’
Map.h file
#ifndef MAP2_H
#define MAP2_H
#include <iostream>
#include <string>
using namespace std;
class Map2 {
public:
Map2(string data1, string data2, string data3, string data4, string data5);
string pop, keyword, user, desc, id;
string get_pop() {return pop;}
string get_key() {return keyword;}
string get_user() {return user;}
string get_desc() {return desc;}
string get_id() {return id;}
void call_Values(int i);
};
Map2:: Map2(string data1, string data2, string data3, string data4, string data5) {
pop = data1;
keyword = data2;
user = data3;
desc = data4;
id = data5;
}
void Map2:: call_Values(int i) {
get_pop();
get_key();
get_user();
get_desc();
get_id();
}
#endif
hash_map2.cpp
#include <fstream>
#include <iostream>
#include "Map2.h"
#include <ext/hash_map>
#include <string>
#include <sstream>
using namespace std;
using __gnu_cxx::hash_multimap;
int nav() {
cout <<"Select from the following options : " << endl <<endl;
cout <<"Search Tweets based on Keyword (Type 1) " <<endl;
cout <<"End Program (Type 2)"<<endl<<endl;
int key =0;
cin >> key;
return key;
}
int main() {
int option = nav();
if (option == 1) {
ifstream readFile("project4.csv");
string tempPop, tempID, tempKey, tempUser, tempDesc;
string tempRead;
hash_multimap<string, Map2 *>map1;
while (readFile != NULL){
// sends to a temp variable
readFile >> tempRead;
for (int i =0; i<400; i++){
//create new object each time
Map2 *mapNode = new Map2(tempPop,tempID,tempKey,tempUser,tempDesc);
//insert each time new object is made
map1.insert(pair<string, Map2 *> (tempKey, mapNode));
} //end for
} //end while
//Navigation through multimap
//first pointer is for first one and second to last hash table value
pair<hash_multimap<string, Map2 *> :: const_iterator,
hash_multimap<string, Map2 *> :: const_iterator> p;
string searchKey = "";
cout << "Please enter the keyword value exactly so we can search the"<<
"available tweets: " <<endl;
cin >> searchKey;
p = map1.equal_range(searchKey);
}
else
return 0;
}
Issue is the fact of calling string to search. Need to convert the string to int before calling the value inside the equal_range function.
The program I want to do is an anagram finder
From a dictionary file and a string input, the function "anagrams" should return me a vector (one for each size from 1 to max) of vector of words found in the dictionary that match all the combination of sub-words possible with input as anagram
When I create the new dictionary with the function createdictionary, I put every anagram in a vector of string
However, when I want to check for these anagrams in my anagrams function, I don't know how to access it (line 74)
In uniqueAnagram, I have every sub-anagram possible, everything is ok, but using
if (dict.words.find(it->second)){
cout << dict.words.find(it->second)->second[0] << endl;
}
in the loop (as a test to see if the expression actually write the right words) leads me to this error and I don't understand why:
In function 'std::vector, std::allocator >, std::allocator, > std::allocator > > >, std::allocator std::char_traits, std::allocator >, std::allocator std::char_traits, std::allocator > > > > > anagrams(const std::string&, const > Dictionary&, int)':|
error: could not convert 'dict->Dictionary::words.std::map<_Key, _Tp,
_Compare, _Alloc>::find [with _Key = std::basic_string, std::allocator >, _Tp =
std::vector,
std::allocator >, std::allocator, std::allocator > > >, _Compare =
std::less,
std::allocator > >, _Alloc = std::allocator
I've been stuck the last 10 hours on this and I can't take it anymore, I really don't know how to solve that issue
Thank for you help, you'd save me life
//anagrams.h
#ifndef ANAGRAMS_H_INCLUDED
#define ANAGRAMS_H_INCLUDED
#include <vector>
#include <string>
#include <map>
using namespace std;
struct Dictionary{
map<string, vector<string> > words;
};
Dictionary createdictionary(const string&);
vector<vector<string> > anagrams(const string&, const Dictionary&, int);
#endif // ANAGRAMS_H_INCLUDED
//anagrams.cpp
#include "anagrams.h"
#include <iostream>
#include <fstream>
#include <map>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
string sortString(string input);
Dictionary createdictionary(const string& filename){
Dictionary dictionary;
ifstream ifs;
ifs.open(filename.c_str());
string word;
while (ifs >> word){
string sortedWord = sortString(word);
(dictionary.words[sortedWord]).push_back(word);
}
return dictionary;
}
string sortString(string input){
vector<char> vectorWord(input.begin(), input.end());
sort(vectorWord.begin(), vectorWord.end());
string sortedInput(vectorWord.begin(), vectorWord.end());
return sortedInput;
}
vector<vector<string> > anagrams(const string& input, const Dictionary& dict, int max){
vector<vector<string> > anagrams;
size_t n = input.length();
for (int r = 0; r < max + 1; r++){
vector<bool> v(n);
fill(v.begin() + r, v.end(), true);
map<string, string> uniqueAnagram;
do {
string word;
for (size_t i = 0; i < n; ++i) {
if (!v[i]) {
word = word + input[i];
}
}
word = sortString(word);
uniqueAnagram[word] = word;
} while (next_permutation(v.begin(), v.end()));
vector<string> tempAnagram;
for(map<string, string>::iterator it = uniqueAnagram.begin(); it != uniqueAnagram.end(); it++){
if (dict.words.find(it->second)){
cout << dict.words.find(it->second)->second[0] << endl;
}
}
sort(tempAnagram.begin(), tempAnagram.end());
anagrams.push_back(tempAnagram);
}
vector<char> vectorWord(input.begin(), input.end());
sort(vectorWord.begin(), vectorWord.end());
string sortedWord(vectorWord.begin(), vectorWord.end());
// cout << (dict.words.find(sortedWord)->second)[0] << endl;
return anagrams;
}
//main.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include "anagrams.h"
using namespace std;
int main()
{
string filename = "C:/dictionary.txt";
Dictionary dictionary = createdictionary(filename);
vector<vector<string> > anagram = anagrams("llohe", dictionary, 5);
return 0;
}
map::find returns an iterator, not a boolean. If the key wasn't found, it returns the map's past-the-end iterator. So instead of
if (dict.words.find(it->second))
you want
if (dict.words.find(it->second) != dict.words.end())
or
if (dict.words.count(it->second) != 0)
It would be more efficient to store the iterator so you don't need to find the key twice:
auto found = dict.words.find(it->second);
if (found != dict.words.end()) {
cout << found->second[0] << endl;
}