Home > Blockchain >  Get all combinations of changing characters in a sring (C )
Get all combinations of changing characters in a sring (C )

Time:07-01

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));
}

Demo

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.

  • Related