Theory
Combinatory is often easier to express with recursive methods (methods that call themselves).
I think that the algorithm is more understandable with an example so let's take String word = hello12
.
We will iterate on each character until a digit is found. The first one is 1
. At this point, we can imagine the word been split in two by a virtual cursor:
hello
is on the left side. We know that it won't change.
12
is on the right side. Each character is likely to be a digit and thus to change.
To retrieve all the possible combinations, we want to:
- Keep the first part of the word
- Compute all the possible combinations of the second part of the word
- Append each of these combinations to the first part of the word
The following tree represents what we want to compute (the root is the first part of the word, each branch represent a combination)
hello
├───1
│ ├───2 (-> hello12)
│ └───@ (-> hello1@)
└───!
├───2 (-> hello!2)
└───@ (-> hello!@)
You want to write an algorithm that gathers all the branches of this tree.
Java Code
/!\ I advise you to try to implement what I described above before taking a look at the code: that's how we improve ourselves!
Here is the corresponding Java code:
public static void main(String[] args) {
Set<String> combinations = combinate("hello12");
combinations.forEach(System.out::println);
}
public static Set<String> combinate(String word) {
// Will hold all the combinations of word
Set<String> combinations = new HashSet<String>();
// The word is a combination (could be ignored if empty, though)
combinations.add(word);
// Iterate on each word's characters
for (int i = 0; i < word.toCharArray().length; i++) {
char character = word.toCharArray()[i];
// If the character should be replaced...
if (Character.isDigit(character)) {
// ... we split the word in two at the character's position & pay attention not be exceed word's length
String firstWordPart = word.substring(0, i);
boolean isWordEnd = i + 1 >= word.length();
String secondWordPart = isWordEnd ? "" : word.substring(i + 1);
// Here is the trick: we compute all combinations of the second word part...
Set<String> allCombinationsOfSecondPart = combinate(secondWordPart);
// ... and we append each of them to the first word part one by one
for (String string : allCombinationsOfSecondPart) {
String combination = firstWordPart + getSpecialSymbol(character) + string;
combinations.add(combination);
}
}
}
return combinations;
}
Please leave a comment if you want me to explain the algorithm further.