Sorting strings with numerical digits in it - c++

I have strings like 7X1234 XY1236 NM1235. I want to sort this strings using last 4 numerical digits only ignoring the initial two alphabets. Also, I want to compare those numerical digits to see if they are sequential.
One way to achieve this I can think of is to split these strings between alphabets and numerals as (7X and 1234) and work lexical cast the numeral string to int and work on it. But, how can I associate the alphabet part again to the numeral part that is how to prefix 7X again to 1234 at the end when the numeral strings are sorted and compared in C++?
In short if I have 7X1234 XY1236 NM1235 BV1238 I need to get 7X1234 NM1235 XY1236 BV1238
I did not elaborate that I wanted to find out if the numerical part of strings are sequential. Right now when I have just ints like 1234 1236 1235 1238 I do something like below
std::vector<int> sortedDigits{1234 1235 1236 1238};
int count = 1;
int pos = 0;
std::vector<std::pair<int, int> > myVec;
myVec.push_back(std::make_pair(sortedDigits[pos], count));
for(size_t i = 1; i < sortedDigits.size(); ++i)
{
if(sortedDigits[i] != (sortedDigits[i-1] + 1))
{
count = 1;
myVec.push_back(std::make_pair(sortedDigits[i], count) );
++pos;
}
else
{
sortedDigits[pos].second = ++count;
}
}
So at the end I get (1234, 3) and (1238, 1)
I don't know how can I get something like this when strings are there?

Since the character encoded values of numerals are ordered in the same order as the numbers they represent, you can do string comparison on the last four digits:
#include <cstring>
#include <string>
// Requires: a.size() >= 2, b.size() >= 2
bool two_less(std::string const & a, std::string const & b)
{
return std::strcmp(a.data() + 2, b.data() + 2) < 0;
}
Now use sort with predicate:
#include <algorithm>
#include <vector>
std::vector<std::string> data { "7X1234", "YX1236" };
std::sort(data.begin(), data.end(), two_less);
In C++11, and in particular if you have no repeated use for this, you can also use a lambda directly in the sort call:
std::sort(data.begin(), data.end(),
[](std::string const & a, std::string const & b)
{ return std::strcmp(a.data() + 2, b.data() + 2) < 0; });
Then you can even make the number "2" a captured variable if you need to vary it.

Use qsort and provide a comparator function that indexes into the start of the string plus an offset of two, rather than directly from the beginning of the string.
For example your comparator function could look like this:
int compare (const void * a, const void * b)
{
char * a_cmp = ((char *)a)+2;
char * b_cmp = ((char *)b)+2;
return strcmp(a_cmp, b_cmp);
}

You can e.g make struct like this
struct combined{
string alph;
int numeral;
};
put these in a c++ standard container
and use the sort of algoritm with a user defined compare object.

You should create a class that encapsulates your string and which has an int and and string field. This class can overload the comparison operators.
class NumberedString
{
private:
int number;
string originalString;
public:
NumberedString(string original) { ... }
friend bool operator> (NumberedString &left, NumberedString &right);
friend bool operator<=(NumberedString &left, NumberedString &right);
friend bool operator< (NumberedString &left, NumberedString &right);
friend bool operator>=(NumberedString &left, NumberedString &right);
};

You can just define your comparator
bool mycomparator(const std::string& a, const std::string& b) {
return a.substr(2) < b.substr(2);
}
then you can sort your std::vector<std::string> passing mycomparator as third parameter.
In C++11 this is also a case in which an anonymous lambda is a good fit...
#include <vector>
#include <algorithm>
#include <string>
#include <iostream>
int main(int argc, const char *argv[])
{
std::vector<std::string> data = {"7X1234", "XY1236", "NM1235", "BV1238"};
std::sort(data.begin(), data.end(),
[](const std::string& a, const std::string& b) {
return a.substr(2) < b.substr(2);
});
for (auto x : data) {
std::cout << x << std::endl;
}
return 0;
}
If you're 100% sure that the strings in the array are in XX9999 format you can use instead
return strncmp(a.data()+2, b.data()+2, 4) < 0;
that is more efficient because doesn't require any memory allocation to do the comparison.

Use a std::map<int, std::string>, using the int value as key and the respective string as value. You can then simply iterate over the map and retrieve the strings; they will already be in sorted order.

How about something like this:
std::string str[] = { "7X1234", "XY1236", "NM1235" };
std::map<int, std::string> m;
for(s : str)
{
std::ostringstream ss(s.substr(2));
int num;
ss >> num;
m[num] = s;
}
for(i : m)
{
std::cout << i->second << " ";
}
std::cout << std::endl;
I just typed this in, so minor typos/bugs may be there, but principle should work.

Related

Check if two given strings are isomorphic to each other c++, not sure why it's wrong

class Solution {
public:
bool isIsomorphic(string s, string t) {
vector <int> sfreq (26,0);
vector <int> tfreq (26,0);
for (int i=0; i<s.size(); i++) {
sfreq[s[i]-'a']++;
tfreq[t[i]-'a']++;
}
if (sfreq != tfreq) {
return false;
}
return true;
}
};
Hi, this is my code in c++, I saw something similar from https://www.geeksforgeeks.org/check-if-two-given-strings-are-isomorphic-to-each-other/ but my answer shows it's wrong. Can anyone please tell me why it's wrong?
You completely misunderstood the description.
Your question suggests that any permutation of characters in input do not change answer. Also you assumed that histograms are equal.
Position of character is important. Each position in both strings creates a unique pair.
Here my code which passed:
class Solution {
public:
static bool canMapInOneDirection(std::string_view s, std::string_view t)
{
const auto n = s.size();
std::array<char, 128> mapping{};
for(size_t i = 0; i < n; ++i) {
if (mapping[s[i]] == 0) mapping[s[i]] = t[i];
else if (mapping[s[i]] != t[i]) return false;
}
return true;
}
bool isIsomorphic(string s, string t)
{
return s.size() == t.size() && canMapInOneDirection(s, t) && canMapInOneDirection(t, s);
}
};
And test cases you can use to test your code:
s
t
answear
"a"
"b"
true
"aa"
"bb"
true
"ab"
"aa"
false
"aabbcc"
"aabcbc"
false
https://godbolt.org/z/61EcTK5fq
This not a question about anagrams or directly about character frequencies. It is about pattern. It's about having a character-by-character mapping that makes one string into the other. AABC is isomorphic to XXYZ but not isomorphic to BCAA.
When we talk about Isomorphism (same form) it's often a good idea to look for a signature representation.
So instead of determining if two strings are isomorphic I've decided to define a unique signature representation and determine isomorphism if two strings map to the same signature.
I've used std::vector<char> for the signature representation such that the first character (if any) is assigned 0 the second (previously unseen) character 1 and so on.
So a string like MOON has signature {0,1,1,2} because the middle characters are the only repeats. MOON is isomorphic to BOOK but not NOON.
The advantage of such a strategy is that if many strings are to be compared to find groups of mutually isomorphic strings each string need only be converted to its signature once.
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
std::vector<char> get_signature(const std::string& str){
std::vector<char> result;
std::unordered_map<char,char> map;
char curr{1};
for(auto cchar : str){
char& c{map[cchar]};
if(c==0){
c=curr++;
}
result.emplace_back(c-1);
}
return result;
}
int check_signature(const std::string& str, const std::vector<char>& expect ){
const auto result{get_signature(str)};
return result==expect?0:1;
}
int main() {
int errors{0};
{
const std::string str{"ABCDE"};
const std::vector<char> signat{0,1,2,3,4};
errors+=check_signature(str,signat);
}
{
const std::string str{"BABY"};
const std::vector<char> signat{0,1,0,2};
errors+=check_signature(str,signat);
}
{
const std::string str{"XXYZX"};
const std::vector<char> signat{0,0,1,2,0};
errors+=check_signature(str,signat);
}
{
const std::string str{"AABCA"};
const std::vector<char> signat{0,0,1,2,0};
errors+=check_signature(str,signat);
}
{
const std::string str{""};
const std::vector<char> signat{};
errors+=check_signature(str,signat);
}
{
const std::string str{"Z"};
const std::vector<char> signat{0};
errors+=check_signature(str,signat);
}
if(get_signature("XXYZX")!=get_signature("AABCA")){
++errors;
}
if(get_signature("MOON")==get_signature("AABCA")){
++errors;
}
if(get_signature("MOON")!=get_signature("BOOK")){
++errors;
}
if(get_signature("MOON")==get_signature("NOON")){
++errors;
}
if(errors!=0){
std::cout << "ERRORS\n";
}else{
std::cout << "SUCCESS\n";
}
return 0;
}
Expected Output: SUCCESS
Because you are missing a loop.
Note that, it still requires more corner case checking to make it fully work. The second approach properly handles all cases.
class Solution {
public:
bool isIsomorphic(string s, string t) {
vector <int> sfreq (26,0);
vector <int> tfreq (26,0);
for (int i=0; i < s.size(); i++) {
sfreq[s[i]-'a']++;
tfreq[t[i]-'a']++;
}
// character at the same index (can be different character) should have the same count.
for(int i= 0; i < s.size(); i++)
if (sfreq[s[i]-'a'] != tfreq[t[i]-'a']) return false;
return true;
}
};
But the above solution only works if there is direct index mappping between characters. Like, AAABBCA and XXXYYZX. But fails for bbbaaaba and aaabbbba. Also, no uppercase, lowercase handled. The link you shared contains the wrong implementation which is mentioned in the comment.
The solution below works as I tested.
class Solution {
public:
bool isIsomorphic(string s, string t) {
vector<int> scount(128, -1), tcount(128, -1);
for (int i = 0; i < s.size(); ++i) {
auto schar = s[i], tchar = t[i];
if (scount[schar] == -1) {
scount[schar] = tchar;
if (tcount[tchar] != -1) return false;
else tcount[tchar] = schar;
} else if (scount[schar] != tchar) return false;
}
return true;
}
};

Sorting string vector using integer values at the end of the string in C++

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(".").

C++ sorting - special characters after normal letters

I have a vector of strings which is holding strings like
BB
aA
12
b
AA
&
[
**
1
Using the default sort() with C++ I am given this sorted list
&
**
1
12
AA
BB
[
aA
b
Instead of that, I need it to be ordered with the normal letters A, a, B, b.... coming first, those followed by "special" characters like 0-9, *, [, , ~, !.... in their normal ASCII order.
I really have no idea how to go about changing the way that the vector is sorted in order to make sure that it is all in that order. Thanks.
Another untested solution.
If I missed a case, I'm sure someone will point it out, but here goes:
#include <iostream>
#include <algorithm>
#include <string>
#include <cctype>
#include <vector>
#include <iterator>
using namespace std;
int main() {
std::vector <std::string> StringVect = { "BB", "aA", "12", "b", "AA", "&", "[", "**", "1" };
std::sort(StringVect.begin(), StringVect.end(), []
(const std::string& s1, const std::string& s2)
{
if (s1.empty() || s2.empty())
return s1 < s2;
// a convenience array
bool ac[] = { isalpha(s1[0]), isalpha(s2[0]),
isdigit(s1[0]), isdigit(s2[0]),
!isalnum(s1[0]), !isalnum(s2[0]) };
// If both strings start with the same type, then return
// s1 < s2
if ((ac[0] && ac[1]) || // if both alpha strings
(ac[2] && ac[3]) || // if both digit strings
(ac[4] && ac[5])) // if both non-alphanumeric strings
return s1 < s2;
// if first string is alpha, or second string is not alphanumeric
// the strings are in order, else they are not
return (ac[0] || ac[5]);
});
copy(StringVect.begin(), StringVect.end(), ostream_iterator<string>(cout, "\n"));
}
Basically, the condition says this:
1) If one of the strings are empty, then return s1 < s2
2) If both strings start with the same character type, just return s1 < s2
3) If the first string starts with an alpha, or if the second string is not an alphanumeric, then the strings are in order and return true, else return false. The trick here is to realize that step 2) eliminated all combinations of both strings being of the same type, so our check at the step 3) stage becomes simplified.
Live example: http://ideone.com/jxxhIY
Edit:
If you are checking for case-insensitive strings, then you need to change the code, and add the case-insensitive check. I won't add the code, since there are multiple ways, both with their respective advantages and disadvantages, of doing a case-insensitive compare.
#include <iostream>
#include <algorithm>
#include <string>
#include <cctype>
#include <vector>
#include <iterator>
using namespace std;
int main() {
std::vector <std::string> StringVect = { "BB", "aA", "12", "b", "AA", "&", "[", "**", "1" };
std::sort(StringVect.begin(), StringVect.end(), []
(const std::string& s1, const std::string& s2)
{
if (s1.empty() || s2.empty())
return s1 < s2;
// a convenience array
bool ac[] = { isalpha(s1[0]), isalpha(s2[0]),
isdigit(s1[0]), isdigit(s2[0]),
!isalnum(s1[0]), !isalnum(s2[0]) };
// If both strings start with the same type, then return
// s1 < s2
if ((ac[2] && ac[3]) || (ac[4] && ac[5]))
return s1 < s2;
// case insensitive
if (ac[0] && ac[1]) // both strings are alpha
return myCaseInsensitiveComp(s1, s2); //returns true if s1 < s2, false otherwise
// if first string is alpha, or second string is not alphanumeric
// the strings are in order, else they are not
return (ac[0] || ac[5]);
});
copy(StringVect.begin(), StringVect.end(), ostream_iterator<string>(cout, "\n"));
}
Again, the myCaseInsensitiveComp is a stub that you should fill in with a function that accomplishes this goal. For a link, see this:
Case insensitive string comparison in C++
You can specify your own function that compares values for the sort to use.
bool myfunction(std::string a, std::string b){ ... }
std::sort(data.begin(), data.end(), myfunction);
Using myfunction you can specify the order you want. Here is more information about sort:
http://www.cplusplus.com/reference/algorithm/sort/
Anyway, you need implement your own comparison logic for it, and use it with sort.
(1) You can give a comparison function object to sort, and implement your own comparison(less than) logic in it, such as:
struct my_comparison {
bool operator()(const string &a, const string &b) {
// implement the less than logic here
}
}
and then:
sort(v.begin(), v.end(), my_comparison());
Reference: http://en.cppreference.com/w/cpp/algorithm/sort
(2) You can implement your own char_traits<char> to make a special string which uses the special comparison logic. Such as:
struct my_char_traits : public char_traits<char>
// just inherit all the other functions
// that we don't need to replace
{
static bool eq(char c1, char c2) {
// implement the comparison logic here
}
static bool lt(char c1, char c2)
// implement the comparison logic here
}
static int compare(const char* s1, const char* s2, size_t n)
// implement the comparison logic here
}
};
And then:
typedef basic_string<char, my_char_traits> my_string;
vector<my_string> v;
// ...
sort(v.begin(), v.end());
Reference: http://en.cppreference.com/w/cpp/string/char_traits
Disclaimer: Untested code.
Here's an implementation of a compare function that should work. The crux of the solution is to re-encode into an array the values corresponding to the characters whose order you want to be changed in the compare function.
void initializeEncoding(char encoding[])
{
for (int i = 0; i < 256; ++i )
{
encoding[i] = i;
}
// Now re-encode for letters, numbers, and other special characters.
// The control characters end at 31. 32 == ' ', the space character.
int nextIndex = 32;
// Re-encode the uppercase letters.
for ( int c = 'A'; c <= 'Z'; ++c, ++nextIndex )
{
encoding[c] = nextIndex;
}
// Re-encode the lowercase letters.
for ( int c = 'a'; c <= 'z'; ++c, ++nextIndex )
{
encoding[c] = nextIndex;
}
// Re-encode the numbers.
for ( int c = '0'; c <= '9'; ++c, ++nextIndex )
{
encoding[c] = nextIndex;
}
// Re-encode the special chracters.
char const* specialChars = " !\"#$%&'()*+,-./:;<=>?[\\]^_`{|}~";
for ( char* cp = specialChars; *cp != '\0'; ++cp, ++nextIndex )
{
encoding[*cp] = nextIndex;
}
}
bool mycompare(char const* s1, char const* s2)
{
static char encoding[256];
static bool initialized = false;
if ( !initialized )
{
initializeEncoding(encoding);
initialized = true;
}
for ( ; *s1 != '\0' && *s2 != '\0'; ++s1, ++s2 )
{
if ( encoding[*s1] != encoding[*s2] )
{
break;
}
}
return ((encoding[*s1] - encoding[*s2]) < 0);
}
bool mycompare(std::string const& s1, std::string const& s2)
{
return mycompare(s1.c_str(), s2.c_str());
}

C++ Finding Anagrams in words

I'm working on a program that looks at whether or not a particular word is an anagram using std:count however, I don't think my function logic is correct and I cannot seem to figure it out.
Assume there are the following words in the file:
Evil
Vile
Veil
Live
My code is as follows:
#include <iostream>
#include <vector>
#include <fstream>
#include <map>
using namespace std;
struct Compare {
std::string str;
Compare(const std::string& str) : str(str) {}
};
bool operator==(const std::pair<int, std::string>&p, const Compare& c) {
return c.str == p.second;
}
bool operator==(const Compare& c, const std::pair<int, std::string>&p) {
return c.str == p.second;
}
std::vector<std::string> readInput(ifstream& file)
{
std::vector<std::string> temp;
string word;
while (file >> word)
{
temp.push_back(word);
}
std::sort(temp.begin(), temp.end());
return temp;
}
int main(int argc, char *argv[]) {
string file = "testing.txt";
ifstream ss(file.c_str());
if(!ss.is_open())
{
cerr << "Cannot open the text file";
}
std::vector<std::string> words = readInput(ss);
std::map<int, std::string> wordsMap;
//std::map<std::string value, int key> values;
for(unsigned i=0; (i < words.size()); i++)
{
wordsMap[i] = words[i];
}
int count = std::count(wordsMap.begin(), wordsMap.end(), Compare("Evil"));
cout << count << endl;
}
I'm pretty sure it's just a case of my logic is wrong in the functions. I hope someone can help :)
The most simple approach would be
To check like following (pseudo code)
bool isAnagram(string s, string t) {return sort(s) == sort(t); }
So, use some think like following, no need of std::map
struct Compare {
std::string str;
Compare(const std::string& x) : str(x) {
std::sort(str.begin(),str.end()); std::transform(str.begin(),
str.end(),str.begin(), ::toupper);}
bool operator ()(const std::string& t)
{
std::string s= t;
std::transform(s.begin(), s.end(),s.begin(), ::toupper);
std::sort(s.begin(),s.end());
return s == str;
}
};
And then
int count = std::count_if(words.begin(), words.end(), Compare("Evil"));
See HERE
This is not the most efficient algorithm, but a quick change to your program that would work could be:
bool operator==(const std::pair<int, std::string>&p, const Compare& c) {
std::string a = c.str;
std::transform(a.begin(), a.end(), a.begin(), ::tolower);
std::sort(a.begin(), a.end());
std::string b = p.second;
std::transform(b.begin(), b.end(), b.begin(), ::tolower);
std::sort(b.begin(), b.end());
return a == b;
}
EDIT: It seems in your present code, you are checking whether the strings are exactly equal to each other (not anagrams).
INSTEAD:
For each word, make an array of 26 elements, each element corresponding to a letter of the alphabet. Parse each word character by character, and increase the count of the particular character in the respective array.
For example for evil, the array would be:
0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0. // It has 1's for letters e,v,i and l
a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z
You make this array for each word that you have. In your case, all the words will have the same array. You then compare these arrays element-wise and proceed accordingly.
Now you just need to see which words have the same corresponding array.
If you want to compare all the N words pair-wise, you can do so using two nested loops in O(N^2) complexity.
The complexity for comparing one pair is O(1).
Complexity of creating the arrays = O(L) where L is the length of the string.
Consider the following:
map<string, set<string>> anagrams;
for (auto word : words)
anagrams[sort(word)].insert(word);
const set<string>& find_anagrams(const string& word)
{
return anagrams[word];
}
When you have a lot of words that are relatively short (or if you can work with large number libs), then you can use a solution similar to what I wrote here -
Generate same unique hash code for all anagrams
Essentially - map each character to a unique prime number (doesn't have to be big, you can map the entire ABC into primes up to 101), and for each word multiply the primes received from it characters. Since multiplication is commutative, anagrams would give the same result, so you just compare that result, hash it, or do whatever you want
Keep in mind that for long words the values would grow pretty fast, so you might need a big numbers lib

How to generate 'consecutive' c++ strings?

I would like to generate consecutive C++ strings like e.g. in cameras: IMG001, IMG002 etc. being able to indicate the prefix and the string length.
I have found a solution where I can generate random strings from concrete character set: link
But I cannot find the thing I want to achieve.
A possible solution:
#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
std::string make_string(const std::string& a_prefix,
size_t a_suffix,
size_t a_max_length)
{
std::ostringstream result;
result << a_prefix <<
std::setfill('0') <<
std::setw(a_max_length - a_prefix.length()) <<
a_suffix;
return result.str();
}
int main()
{
for (size_t i = 0; i < 100; i++)
{
std::cout << make_string("IMG", i, 6) << "\n";
}
return 0;
}
See online demo at http://ideone.com/HZWmtI.
Something like this would work
#include <string>
#include <iomanip>
#include <sstream>
std::string GetNextNumber( int &lastNum )
{
std::stringstream ss;
ss << "IMG";
ss << std::setfill('0') << std::setw(3) << lastNum++;
return ss.str();
}
int main()
{
int x = 1;
std::string s = GetNextNumber( x );
s = GetNextNumber( x );
return 0;
}
You can call GetNextNumber repeatedly with an int reference to generate new image numbers. You can always use sprintf but it won't be the c++ way :)
const int max_size = 7 + 1; // maximum size of the name plus one
char buf[max_size];
for (int i = 0 ; i < 1000; ++i) {
sprintf(buf, "IMG%.04d", i);
printf("The next name is %s\n", buf);
}
char * seq_gen(char * prefix) {
static int counter;
char * result;
sprintf(result, "%s%03d", prefix, counter++);
return result;
}
This would print your prefix with 3 digit padding string. If you want a lengthy string, all you have to do is provide the prefix as much as needed and change the %03d in the above code to whatever length of digit padding you want.
Well, the idea is rather simple. Just store the current number and increment it each time new string is generated. You can implement it to model an iterator to reduce the fluff in using it (you can then use standard algorithms with it). Using Boost.Iterator (it should work with any string type, too):
#include <boost/iterator/iterator_facade.hpp>
#include <sstream>
#include <iomanip>
// can't come up with a better name
template <typename StringT, typename OrdT>
struct ordinal_id_generator : boost::iterator_facade<
ordinal_id_generator<StringT, OrdT>, StringT,
boost::forward_traversal_tag, StringT
> {
ordinal_id_generator(
const StringT& prefix = StringT(),
typename StringT::size_type suffix_length = 5, OrdT initial = 0
) : prefix(prefix), suffix_length(suffix_length), ordinal(initial)
{}
private:
StringT prefix;
typename StringT::size_type suffix_length;
OrdT ordinal;
friend class boost::iterator_core_access;
void increment() {
++ordinal;
}
bool equal(const ordinal_id_generator& other) const {
return (
ordinal == other.ordinal
&& prefix == other.prefix
&& suffix_length == other.suffix_length
);
}
StringT dereference() const {
std::basic_ostringstream<typename StringT::value_type> ss;
ss << prefix << std::setfill('0')
<< std::setw(suffix_length) << ordinal;
return ss.str();
}
};
And example code:
#include <string>
#include <iostream>
#include <iterator>
#include <algorithm>
typedef ordinal_id_generator<std::string, unsigned> generator;
int main() {
std::ostream_iterator<std::string> out(std::cout, "\n");
std::copy_n(generator("IMG"), 5, out);
// can even behave as a range
std::copy(generator("foo", 1, 2), generator("foo", 1, 4), out);
return 0;
}
Take a look at the standard library's string streams. Have an integer that you increment, and insert into the string stream after every increment. To control the string length, there's the concept of fill characters, and the width() member function.
You have many ways of doing that.
The generic one would be to, like the link that you showed, have an array of possible characters. Then after each iteration, you start from right-most character, increment it (that is, change it to the next one in the possible characters list) and if it overflowed, set it to the first one (index 0) and go the one on the left. This is exactly like incrementing a number in base, say 62.
In your specific example, you are better off with creating the string from another string and a number.
If you like *printf, you can write a string with "IMG%04d" and have the parameter go from 0 to whatever.
If you like stringstream, you can similarly do so.
What exactly do you mean by consecutive strings ?
Since you've mentioned that you're using C++ strings, try using the .string::append method.
string str, str2;
str.append("A");
str.append(str2);
Lookup http://www.cplusplus.com/reference/string/string/append/ for more overloaded calls of the append function.
it's pseudo code. you'll understand what i mean :D
int counter = 0, retval;
do
{
char filename[MAX_PATH];
sprintf(filename, "IMG00%d", counter++);
if(retval = CreateFile(...))
//ok, return
}while(!retval);
You have to keep a counter that is increased everytime you get a new name. This counter has to be saved when your application is ends, and loaded when you application starts.
Could be something like this:
class NameGenerator
{
public:
NameGenerator()
: m_counter(0)
{
// Code to load the counter from a file
}
~NameGenerator()
{
// Code to save the counter to a file
}
std::string get_next_name()
{
// Combine your preferred prefix with your counter
// Increase the counter
// Return the string
}
private:
int m_counter;
}
NameGenerator my_name_generator;
Then use it like this:
std::string my_name = my_name_generator.get_next_name();