Okay, I've been tearing my hair out for the past 5 hours on this.
I'm still new to C , so bear with me if this is a stupid question.
I would like to get all possible character combinations, and put them in a string, in C .
void changeCharInString(std::string &str, int index, char ch)
{
str[index] = ch;
}
for (int i = 0; i < globals::numOfValidChars; i )
//numOfValidChars is just sizeof validChars
{
changeCharInString(test, 0, globals::validChars[i]);
//validChars is valid characters, which is an array of lowercase letters and some symbols
//'test' is just my testing string for this.
}
If I wanted all combinations of a three-letter string, manually, what I would do would be:
for (int a = 0; a < globals::numOfValidChars; a )
{
changeCharInString(test, 0, globals::validChars[a]);
//index 0 because first for loop
//do something with string 'test'
for (int b = 0; b < globals::numOfValidChars; b )
{
changeCharInString(test, 1, globals::validChars[b]);
//index 1 because second second loop
//do something with string 'test'
for (int c = 0; c < globals::numOfValidChars; c )
{
changeCharInString(test, 2, globals::validChars[c]);
//index 2 because third for loop
//do something with string 'test'
}
}
}
This would print me "aaa"
to "___"
('_'
being the last special character in my validChars
array), including duplicates, e.g. "aa_"
, "a_a"
, and "_aa"
.
But, as with all programmers,I want some efficiency, and don't want to manually type out the for loops for myself.
My question: does anyone have a method of doing this? I've seen some posts on Google, but didn't really understand them, including that most, if not all, were without duplicates...
Thank you all. I hope this wasn't too much of a bother to read <3
CodePudding user response:
As you said hard coded way would be something like
for (char c1 : globals::validChars) {
for (char c2 : globals::validChars) {
for (char c3 : globals::validChars) {
std::string s {c1, c2, c3};
do_job(s);
}
}
}
or with range-v3
for (auto t : ranges::view::cartesian_product(globals::validChars,
globals::validChars,
globals::validChars)) {
std::string s{ std::get<0>(t), std::get<1>(t), std::get<2>(t)};
do_job(s);
// std::apply([](auto... args){ std::string s{args...}; do_job(s); }, t); // C 17
}
For arbitrary size, you might do
bool increase(const std::vector<char>& alphabet,
std::string& s,
std::vector<std::size_t>& it)
{
for (std::size_t i = 0, size = it.size(); i != size; i) {
const std::size_t index = size - 1 - i;
it[index];
if (it[index] >= alphabet.size()) {
it[index] = 0;
s[index] = alphabet[it[index]];
} else {
s[index] = alphabet[it[index]];
return true;
}
}
return false;
}
template <typename F>
void iterate(F do_job, const std::vector<char>& alphabet, std::size_t size)
{
std::vector<std::size_t> it(size, 0);
std::string s(size, alphabet[0]);
do {
do_job(s);
} while (increase(alphabet, s, it));
}
CodePudding user response:
You tagged your question as "recursion", so here is an answer that uses recursion:
void generate(int idx, string& test) {
if (idx == test.size()) {
// do something with test
} else {
for (char c : globals::validChars) {
test[idx] = c;
generate(idx 1, test);
}
}
}
If the size is fixed and known at compile-time, you can use templates and if it is not too large the compiler will create a nice nested loop for you:
template <size_t idx>
void generate(string& test) {
for (char c : globals::validChars) {
test[test.size()-idx] = c;
generate<idx-1>(test);
}
}
template <>
void generate<0>(string& test) {
// do something with test
}
int main() {
string test("aaa");
generate<3>(test);
}
CodePudding user response:
Jarod42 is right.
By the way, you should also use the interface that std::string
is providing for replacing the characters.