I have a 2D array that stores different letters that correspond to what you would see on a phone keypad.
char [][] convert =
{ {},
{'A','B','C'},
{'D','E','F'},
{'G','H','I'},
{'J','K','L'},
{'M','N','O'},
{'P','R','S'},
{'T','U','V'},
{'W','X','Y'}
};
If I want to find all the possible permutations of a 5 letter word that takes 1 letter each from the first 5 rows of the 2D array, how would I do that? I am thinking about recursion but it's just confusing me.
To make this problem easier to understand, here's an example:
A 3 letter word takes its first letter from row 1, {'A','B','C'}, its second letter from row 3, {'G','H','I'}, and its third letter from row 6, {'P','R','S'}. There would be in total 27 possible outcomes: AGP AGR AGS AHP AHR AHS AIP AIR AIS BGP BGR BGS BHP BHR BHS BIP BIR BIS CGP CGR CGS CHP CHR CHS CIP CIR CIS.
The first thing to observe is that if you are making words by selecting one of 3 characters from each of 5 rows, you will end with 35 = 243 words in total. Regardless of how you implement the program, it must end up building each of those 243 words.
Recursion is a good implementation strategy because it makes it clear that you're selecting one of the three characters in the first row, and for each of those choices you go on to select one of the three characters in the second row, and so on.
In the Java program below, the first version of makeWord is a recursive function that selects a character in the row indexed by currentRowIndex and appends that character to wordBuffer. If this is the last row, the word is complete and it gets appended to a list of words. Otherwise, the function calls itself to work on row currentRowIndex + 1.
Notice that the current state of wordBuffer carries through to the recursive call. Only after returning from the recursive call do we delete the last character from wordBuffer.
The second version of makeWord lets you pass an array of row indices that specify which rows you want to select characters from. For example, to select characters from rows 1, 3, and 6, you would call:
permuter.makeWord(new int[]{ 1, 3, 6 }, 0);
You can substitute that call in the main method instead of the current line, which causes a word to be built with characters from rows 1 through 5:
permuter.makeWord(1, 5);
If you take a close look at the makeWord methods, you'll see that the first one doesn't recurse when the string is complete, while the second one recurses once and then returns early because position == indices.length. The latter approach is slightly less efficient because it costs one more recursive call, but you may find that it expresses the concept of recursion more clearly. It's a matter of taste.
import java.util.*;
public class PermuteCharacters {
char[][] rows = {
{},
{'A','B','C'},
{'D','E','F'},
{'G','H','I'},
{'J','K','L'},
{'M','N','O'},
{'P','R','S'},
{'T','U','V'},
{'W','X','Y'}
};
StringBuffer wordBuffer = new StringBuffer();
ArrayList<String> words = new ArrayList<String>();
void makeWord(int currentRowIndex, int endRowIndex) {
char[] row = rows[currentRowIndex];
for (int i = 0; i < row.length; ++i) {
wordBuffer.append(row[i]);
if (currentRowIndex == endRowIndex) {
words.add(wordBuffer.toString());
} else {
makeWord(currentRowIndex + 1, endRowIndex);
}
wordBuffer.deleteCharAt(wordBuffer.length() - 1);
}
}
void makeWord(int[] indices, int position) {
if (position == indices.length) {
words.add(wordBuffer.toString());
return;
}
char[] row = rows[indices[position]];
for (int i = 0; i < row.length; ++i) {
wordBuffer.append(row[i]);
makeWord(indices, position + 1);
wordBuffer.deleteCharAt(wordBuffer.length() - 1);
}
}
void displayWords() {
if (words.size() != 0) {
System.out.print(words.get(0));
for (int i = 1; i < words.size(); ++i) {
System.out.print(" " + words.get(i));
}
System.out.println();
}
System.out.println(words.size() + " words");
}
public static void main(String[] args) {
PermuteCharacters permuter = new PermuteCharacters();
permuter.makeWord(1, 5);
permuter.displayWords();
}
}
Try this, if you can use Java8
static Stream<String> stream(char[] chars) {
return new String(chars).chars().mapToObj(c -> Character.toString((char)c));
}
public static void main(String[] args) {
char [][] convert =
{ {},
{'A','B','C'},
{'D','E','F'},
{'G','H','I'},
{'J','K','L'},
{'M','N','O'},
{'P','R','S'},
{'T','U','V'},
{'W','X','Y'}
};
stream(convert[1])
.flatMap(s -> stream(convert[2]).map(t -> s + t))
.flatMap(s -> stream(convert[3]).map(t -> s + t))
.flatMap(s -> stream(convert[4]).map(t -> s + t))
.flatMap(s -> stream(convert[5]).map(t -> s + t))
.forEach(x -> System.out.println(x));
}
You can also write recursive version.
static Stream<String> permutation(char[][] convert, int row) {
return row == 1
? stream(convert[1])
: permutation(convert, row - 1)
.flatMap(s -> stream(convert[row]).map(t -> s + t));
}
And call this.
permutation(convert, 5)
.forEach(x -> System.out.println(x));
This can be solved using dynamic programming approach.
Take the first two rows and combine all the strings in the arrays. That will give you a resultant array of size m*n. Where m is the size of the first array and n is the size of the second array. In your case it is 9. Then assign the resultant array to the first array and assign the third array to the second array. Repeat it till the fifth array. That will give you all possible strings from the first five arrays.
public static String[] getAllCombinations(String array[][], int l) {
String a[] = array[0];
String temp[]=null;
for(int i=1;i<l;i++) {
int z=0;
String b[] = array[i];
temp = new String[a.length*b.length];
for(String x:a) {
for(String y:b) {
temp[z] = ""+x+y;
z++;
}
}
a = temp;
}
System.out.println(temp.length);
return temp;
}
This function should do it.
Here is one possible solution.
You first define the sequence of keys to be pressed. For example, if you want to take one letter each from the first five rows of the array, the sequence would be (1, 2, 3, 4, 5) (since the first row is empty). If you want to spell "stack", the sequence would be (6, 7, 1, 1, 4).
Then you loop through the sequence. In each step, you get the array of chars that correspond to this position of the sequence. Then you generate all the words that result from all the combinations so far, which are all the words from the previous step combined with all the chars of the current step.
Finally, the result of the last step is the final result that contains all the possible words.
char [][] convert = {
{}, // 0
{'A','B','C'}, // 1
{'D','E','F'}, // 2
{'G','H','I'}, // 3
{'J','K','L'}, // 4
{'M','N','O'}, // 5
{'P','R','S'}, // 6
{'T','U','V'}, // 7
{'W','X','Y'} // 8
};
// Sequence of keys to be pressed. In this case the pressed keys are
// [ABC], [DEF], [GHI]
int[] sequence = new int[] {1, 2, 3};
// List for storing the results of each level.
List<List<String>> results = new ArrayList<>();
for(int i=0; i<sequence.length; i++){
// Results of this level
List<String> words = new ArrayList<>();
results.add(words);
List<String> prevLevelWords;
if(i==0){
prevLevelWords = Collections.singletonList("");
} else {
prevLevelWords = results.get(i-1);
}
char[] thisLevelChars = convert[sequence[i]];
if(thisLevelChars.length == 0){
words.addAll(prevLevelWords);
} else {
for(String word : prevLevelWords){
for(char ch : convert[sequence[i]]){
words.add(word + ch);
}
}
}
}
List<String> finalResult = results.get(sequence.length-1);
for(String word : finalResult) {
System.out.println(word);
}
Run it
Here's an alternate solution using Java 8 streams for your interest.
public class Combiner {
private List<String> combos = new ArrayList<>();
public Stream<String> stream() {
return combos.stream();
}
public void accept(char[] values) {
if (combos.isEmpty()) {
for (char value : values) {
combos.add(String.valueOf(value));
}
} else {
Combiner other = new Combiner();
other.accept(values);
combine(other);
}
}
public Combiner combine(Combiner other) {
combos = stream()
.flatMap(v1 -> other.stream().map(v2 -> v1 + v2))
.collect(Collectors.toList());
return this;
}
}
Essentially this is a collector that takes each element in the stream and adds a new combination for every concatenation of the items in the element and the existing combinations.
And here's some sample code showing how to use it:
public static void main(String[] args) {
char[][] vals = {{'a', 'b'}, {'c'}, {'d', 'e'}, {'f', 'g', 'h'}};
Arrays.stream(vals).parallel()
.collect(Combiner::new, Combiner::accept, Combiner::combine)
.stream().forEach(System.out::println);
}
The parallel isn't strictly necessary: it's just to show that for massive numbers of combinations the stream can be split across processors and then combined.
The code is a lot simpler for other types of data that can be naturally streamed. Unfortunately there's no Arrays.stream(char[]) so the traditional iteration makes the code a bit clearer. You can use something ugly like converting to string then an IntStream and then to Stream<Character> but frankly that's a lot of work to avoid iterating over the array :-)
Related
String
input="4,8,222cd,77,77A";
I sorted the values.for example, I sorted 3,4,5,6 consecutive values in the range [3-6].I have done consecutive numbers "[]" in this format but I couldn't make the letters. Sorted list should not be disrupted.
Here is an example method to merge the letters at the end:
static List<String> mergeLetters(List<String> items){
Map<String, List<String>> mergingCandidates = new HashMap<>();
for (String s : items) {
int length = s.length();
if (length > 1 && Character.isLetter(s.charAt(length - 1))) {
String key = s.substring(0, length - 1);
String value = s.substring(length - 1);
List<String> tmp = mergingCandidates.computeIfAbsent(key, k -> new LinkedList<String>());
tmp.add(value);
}
}
for (Map.Entry<String, List<String>> entry : mergingCandidates.entrySet()) {
String key = entry.getKey();
List<String> characterList = entry.getValue();
if (characterList.size() > 1) {
boolean consecutive = true;
Character[] characters = characterList.stream().map(s -> s.charAt(0)).toArray(Character[]::new);
for (int i = 0; i < characters.length - 1; i++) {
if (characters[i + 1] - characters[i] != 1) {
consecutive = false;
break;
}
}
if (consecutive) {
int indexToBeReplaced = items.indexOf(key + characterList.get(0));
String newValue = key + "[" + characterList.get(0)
+ "-" + characterList.get(characterList.size() - 1) + "]";
characterList.forEach(letter -> items.remove(key + letter));
items.add(indexToBeReplaced, newValue);
}
}
}
return items;
}
Explanation:
You find all the sequences that differ only on the last letter and make a Map out of them.
You pick the Map entries that have 2 or more ending letters.
You check if the letters are consecutive.
If yes, remove them and add a new merged entry instead. Be careful to add the new entry at the same index.
The main method should sort the items, first merge the digits as you correctly do now, and then replace the letters that can be merged:
String input = "605,2A,401-2A,32C,21F,201A,605A,401-1A,200-2E,462A,462,583-58D,200,462-1A,583/58E,583-57D, 542,2B, 1, 542/2E, 801, 802, 803, 605B, 32D, 3, 603, 4, 6, 5, 60, 201C, 542/2D,40,20,2C,800,800-1,50,200-2C,21C,800A,200A,571-573L,51/2,470/1,51/1,571-573K,454-1,444-446,571-573M";
List<String> items = Arrays.asList(input.split("\\s*,\\s*"));
items.sort(Comparator.naturalOrder());
items.sort(Comparator.comparingInt(o -> Integer.parseInt(o.split("\\D")[0])));
List<String> merged = mergeRanges(items);
List<String> mergeLetters = mergeLetters(merged);
System.out.println(("Result: " + mergeLetters));
The output for your input is:
Result: [1, 2[A-C], [3-6], 20, 21C, 21F, 32[C-D], 40, 50, 51/1, 51/2, 60, 200, 200-2C, 200-2E, 200A, 201A, 201C, 401-1A, 401-2A, 444-446, 454-1, 462, 462-1A, 462A, 470/1, 542, 542/2[D-E], 571-573[K-M], 583-57D, 583-58D, 583/58E, 603, 605, 605[A-B], 800, 800-1, 800A, [801-803]]
The general idea is first to expand all the declarations in the input, then sort them, then to emit them again, while compacting ranges when you encounter successive items,.
Expand all declarations: 571-573K (if I understand correctly) would expand to the three items 571K, 572K, 573K. Most simple declarations just expand to themselves.
Sort: You need a sort function that is sensitive to both numbers and letters. It will probably have to break up the input string and compre the number part numerically, but the letter part alphabetically.
Emit: Walk the sorted list and emit each item; however, if the next item is adjacent to the current item, merge it with the current item instead. This will probably require that you create a temporary list of outputs, and delay the actual output until you've gone through the original list.
Im trying to keep track of occurrences of strings that start with certain letters in an array. Given the following array, count the number of occurrences of strings that start with "T", "B, and "A" for example..
String[] month_codes = {"July 2007", "TRUCKS", "APPLES", "BEANS", "COATS", "BANANA", "CODES"}
Output would look like this for each month:
T A B
July 2 1 2
Augu ....
Sept ....
This is the code I used but it only captures one occurrence
public static int extract_counts(String[] month_array){
for(int i=0; i < month_array.length; i++){
System.out.println(month_array[i]);
if(month_array[i].startsWith("T")){
july_ab++;
}
}
return july_ab;
}
What I would do is iterate through all of the items once, counting the number of occurrences. Then I'd print the results.
The easiest way to do this, in my opinion, would be to use an array of integers representing the 26 letters in the English alphabet:
int[] frequency = new int[26];
// frequency[0] is the number of words that start with A
Here, we can take advantage of the fact that Java initializes an integer arrays with all zeroes.
Then we'd iterate through the month_array and count how many words start with each letter.
for( String word : month_array ) {
char firstLetter = word.toLowerCase().charAt(0);
int index = firstLetter - 'a'; // a = 0, b = 1, etc.
frequency[index]++;
}
At the end of this for-loop, the frequency array now contains how many words start with each letter. We could print them like this:
for(int i = 0; i < 26; i++) {
System.out.print( frequency[i] );
}
Of course, this would be in order from A to Z. You'd also probably want to print the headers with the letters A-Z first before iterating through your months.
In your example, your first word is "July 2007" -- of course you'd need to omit this first item before counting since you'd otherwise be artificially incrementing (for example) the 'J' counter.
public int[] extractCounts( String[] month_array ) {
int[] frequency = new int[26];
for(int i = 1; i < month_array.length; i++) {
// here, we start at 1 to omit the 'July 2007' record
String word = month_array[i];
char firstLetter = word.toLowerCase().charAt(0);
int index = firstLetter - 'a';
frequency[index]++;
}
return frequency;
}
public void printCounts(String month, int[] frequencies) {
System.out.print(month + "\t");
for(int frequency : frequencies) {
System.out.print(frequency);
System.out.print("\t");
}
}
String[] month_codes = ...;
Map<Character, Long> result = Arrays.stream(month_codes)
.collect(Collectors.groupingBy(
str -> Character.toLowerCase(str.charAt(0)),
Collectors.counting()));
So, Map<Character, Long> contains following data:
'a' -> 1
'b' -> 2
'c' -> 2
't' -> 1
'j' -> 1
You can do it in several ways.
I am providing you a way which can find the occurrences (Starting chars) using single loop and Map to store only occurrence of String start with "A", "B" and "T".
Here is the code:
public static Map<String, Integer> extract_counts(String[] month_array){
Map<String, Integer> map = new HashMap<>();
for(int i=0; i < month_array.length; i++){
//System.out.println(month_array[i]);
if(month_array[i].startsWith("T")){
if(map.containsKey("T")){
map.put("T", map.get("T") + 1);
}else {
map.put("T", 1);
}
}
if(month_array[i].startsWith("A")){
if(map.containsKey("A")){
map.put("A", map.get("A") + 1);
}else {
map.put("A", 1);
}
}
if(month_array[i].startsWith("B")){
if(map.containsKey("B")){
map.put("B", map.get("B") + 1);
}else {
map.put("B", 1);
}
}
}
return map;
Calling the method:
String[] month_codes = {"July 2007", "TRUCKS", "APPLES", "BEANS", "COATS", "BANANA", "CODES"};
System.out.println(extract_counts(month_codes));
Will get output like this:
{A=1, B=2, T=1}
In my country there is Game Show called Slagalica where one of the tasks is to find longest word in array of 12 letters. Size of the longest word is always 10, 11 or 12.
I have file with words from my language I use as database. Words that have 10, 11 or 12 letters in them I've saved in List (listWordSize10_11_12).
When I enter jumbled word [12 letters] in I want from my program to find what word is that originally. I know how to make it work when jumbled word is 12 letters word but I can't work it out when it's less.
Example: 10 letter word is jumbled + 2 random letters.
Goal is for that 10 letter word to be recognized and printed in original state.
Where is what I've done:
// un-jumbling word
System.out.println("Unesite rijec koja treba da se desifruje: ");
String jumbledWord = tast.nextLine();
char[] letter = jumbledWord.toCharArray();
Arrays.sort(letter);
String sorted_Mistery_Word = new String(letter);
for (int i = 0; i < listWordSize10_11_12.size(); i++) {
int exception = 0;
char[] letter_2 = listWordSize10_11_12.get(i).toCharArray();
Arrays.sort(letter_2);
String longWords = new String(letter_2);
int j = i;
while(longWords.length()>i){
if(sorted_Mistery_Word.charAt(j)!=longWords.charAt(i)){
exception++;
j++;
}
}
if(exception < 3){
System.out.println("Your word is: "+listWordSize10_11_12.get(i));
break;
}
}
Thanks!!!
P.S. This is not a homework or some job, just project I've been doing for fun!
Thanks everyone for the help, I've learned a lot!
Your basic approach for 12 characters, which I would characterize as fingerprinting, will also work for 10 or 11 letter words with some modification.
That is, rather that just sorting the letters in each candidate word as you examine it to create the fingerprint, pre-process your array to create a small(ish) fingerprint of each word as a byte[]. Using the English alphabet and ignoring case, for example, you could create a 26-byte array for each word, where each byte position contained the count of each letter in the word.
That is fingerprint[0] contains the number of 'a' characters in the word, and fingerprint[25] the number of 'z' characters.
Then just replace your sorted_Mistery_Word.charAt(j)!=longWords.charAt(i) check with a loop that increments a temporary array for each letter in the mystery word. Finally, check that the temporary array has at least the same value for every position. Something like:
byte [] makeFingerprint(String s) {
byte[] fingerprint = new byte[26];
for (char c : letter_2) {
fingerprint[c - 'a']++;
}
return fingerprint;
}
/** determine if sub is a subset of super */
boolean isSubset(byte[] sub, byte[] super) {
for (int i=0; i < sub.length; i++) {
if (sub[i] > super[i])
return false;
}
return true;
}
void findMatch(String jumbledWord) {
byte[] fingerprint = makeFingerprint(jumbledWord);
for (byte[] candidate : fingerprintList) {
if (isSubset(fingerprint, candidate)) {
System.out.println("Your word is: " + ...);
break;
}
}
}
Here I omitted the creation of the fingerprintList - but it just involves fingerprint each word.
There are plenty of optimizations possible, but this should already be quite a bit faster than your version (and is "garbage free" in the main loop). It can handle candidates of any length (not just 10-12 chars). The biggest optimization, if you will check many words, is to try to use the "fingerpint" as a key for direct lookup. For the 12 character case this is trivial (direct lookup), but for the 10 and 11 or this you would likely have to use type of technique for higher-dimensional searching - locality sensitive hashing seems like a natural fit.
Here's one way to go about the problem. Let's say (since no example input was provided) that there's 3 String[]'s, arr3 (that represents the 10-letter-words you have), arr4 (that represents the 11-letter-words you have), and arr5 (you guessed it, represents the 12-letter words you have). This is what I made for those:
String[] arr3 = { "map", "cat", "dog" };
String[] arr4 = { "four", "five", "nine" };
String[] arr5 = { "funny", "comma", "brace" };
So based on what you said, if we got the input of pam, we'd want the output of map. If we got the input of enni we'd want the output of nine. And if we got the input of yfunn we'd want the output of funny. So how to go about doing this? I like what #cricket_007 mentioned about using maps. But first, let's get the permutations down.
Based off of the linked SO question, I came up with this modified/variation to get the jumbled text:
public static List<String> jumble(String str) {
List<String> result = new ArrayList<String>();
permutation(result, "", str);
return result;
}
private static void permutation(List<String> l, String prefix, String s) {
int n = s.length();
if (n == 0) {
l.add(prefix);
} else {
for (int i = 0; i < n; i++)
permutation(l, prefix + s.charAt(i), s.substring(0, i) + s.substring(i + 1, n));
}
}
This code will let us easily construct a single map for us to store jumbled text in as a key, and the answer to that jumbled text as a value.
All together, the final code looks like this:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Appy {
public static void main(String[] args) {
String[] arr3 = { "map", "cat", "dog" };
String[] arr4 = { "four", "five", "nine" };
String[] arr5 = { "funny", "comma", "brace" };
List<String> permutations = new ArrayList<String>();
Map<String, String> map = new HashMap<String, String>();
for (String s : arr3) {
permutations = jumble(s);
for (String str : permutations)
map.put(str, s);
}
for (String s : arr4) {
permutations = jumble(s);
for (String str : permutations)
map.put(str, s);
}
for (String s : arr5) {
permutations = jumble(s);
for (String str : permutations)
map.put(str, s);
}
System.out.println("test = 'pam' -> " + map.get("pam"));
System.out.println("test = 'enni' -> " + map.get("enni"));
System.out.println("test = 'yfunn' -> " + map.get("yfunn"));
}
public static List<String> jumble(String str) {
List<String> result = new ArrayList<String>();
permutation(result, "", str);
return result;
}
private static void permutation(List<String> l, String prefix, String s) {
int n = s.length();
if (n == 0) {
l.add(prefix);
} else {
for (int i = 0; i < n; i++)
permutation(l, prefix + s.charAt(i), s.substring(0, i) + s.substring(i + 1, n));
}
}
}
Which gives the resulting output of:
test = 'pam' -> map
test = 'enni' -> nine
test = 'yfunn' -> funny
Now applying this logic, adapting for your case of 10, 11, and 12 letter words should be relatively simple. Cheers!
I am building a mind game, it's hard to explain so I put an example.
I have a list of words (it can be infinite):
String myList[] = {"chair", "house", "ocean", "plane", "dog", "TV", "grass", "money" etc....}
Now the tricky part, I need to build 4 lists of pair index/word (every list has the same size) randomly but that fit these rule:
if I chose a number, the word that match this number only appears in 2 lists.
for example this would be correct:
List1:
1/chair
2/house
3/plane
4/grass
List2
1/chair
2/dog
3/plane
4/TV
List3:
1/ocean
2/house
3/money
4/TV
List4
1/ocean
2/dog
3/money
4/grass
For example:
If I pick number 3, then list 3 and list 4 match the word 'money', list 1 and 2 match the word 'plane'. There always must be 2 matching lists (never less, never more). They should be build from a huge array of words randomly, so you can't guess which list will be matching when you pick a number.
I tried to do it with a nice simple recursive algorithm. But I badly failed.
My initial approach to this problem would be to
Select a random word in the universe
Assign the selected word to two lists that
are different and
are not full
Store the random word in a set of closed words in case it is selected again
Rinse and repeat until all lists are full
Something like this should do the trick (you can change the provided words and the respective listSize accordingly). The number of words should be dividable by the listSize in order to fill up all lists.
public static void main(String[] args) {
String[] words = new String[] { "chair", "house", "ocean", "plane",
"dog", "TV", "grass", "money" };
// valid list sizes for 8 words: 1, 2, 4, 8
int listSize = 4;
List<String[]> result = distributeRandomly(words, listSize);
for (String[] resultList : result) {
for (int index = 0; index < listSize; index++) {
System.out.println((index + 1) + "/" + resultList[index]);
}
System.out.println();
}
}
private static List<String[]> distributeRandomly(String[] words, int listSize) {
// each word goes into 2 lists, so how many lists do we need?
int listCount = words.length * 2 / listSize;
if (listCount * listSize != words.length * 2) {
throw new IllegalArgumentException("Number of words"
+ " must be a multiple of the size of the individual lists!");
}
// initialize result lists (here arrays) in fitting size
List<String[]> listsToFill = new ArrayList<String[]>(listCount);
for (int index = 0; index < listCount; index++) {
listsToFill.add(new String[listSize]);
}
// be sure to randomly pick the given words by shuffling them
List<String> shuffledWords = new ArrayList<String>(Arrays.asList(words));
Collections.shuffle(shuffledWords);
List<String[]> result = new ArrayList<String[]>(listCount);
int maxWordPosition = listSize - 1;
// distribute words
for (String word : shuffledWords) {
// word is supposed to be inserted in two lists at the same index
int wordPosition = -1;
// iterate result lists
Iterator<String[]> listIterator = listsToFill.iterator();
while (listIterator.hasNext()) {
String[] list = listIterator.next();
if (wordPosition == -1) {
// look out for the first list with an empty slot
for (int index = 0; index < listSize; index++) {
if (list[index] == null) {
// found empty slot at this index
wordPosition = index;
// insert word here (first list)
list[wordPosition] = word;
if (wordPosition == maxWordPosition) {
// the list is full, no more empty slots here
listIterator.remove();
result.add(list);
}
break;
}
}
} else if (list[wordPosition] == null) {
// found second list with an empty slot at the same index
list[wordPosition] = word;
if (wordPosition == maxWordPosition) {
// the list is full, no more empty slots here
listIterator.remove();
result.add(list);
}
// we are done with this word
break;
}
}
// shuffle result lists again, to ensure randomness
Collections.shuffle(listsToFill);
}
return result;
}
This produces (for example) the following output:
1/grass
2/TV
3/plane
4/ocean
1/grass
2/dog
3/money
4/chair
1/house
2/dog
3/money
4/ocean
1/house
2/TV
3/plane
4/chair
Given a string, I want to find all variants without transposition, only deletion. For example, given the string:
helloo
The list of variants would be as follows (separated by white space).
helloo hello heloo helo
My solution so far is to move through each character, and then if the current character matches the next character, recursively try the original and the deleted character version, as follows.
// takes String with at most two consecutive characters of any character,
// and returns an Iterable of all possible variants (e.g. hheello -> heello, hhello, ...)
private static Iterable<String> findAllVariants(String word) {
StringBuilder variant = new StringBuilder(word);
Queue<String> q = new LinkedList<String>();
findAllVariants(word, variant, 0, q);
return q;
}
// helper method
private static void findAllVariants(String word, StringBuilder variant, int currIndex, Queue<String> q) {
if (currIndex == variant.length() - 1) q.add(variant.toString());
for (int i = currIndex; i < variant.length() - 1; i++) {
char thisChar = variant.charAt(i);
char nextChar = variant.charAt(i+1);
if (thisChar == nextChar) {
// get all variants with repeat character
findAllVariants(word, variant, i+1, q);
// get all variants without repeat character;
variant = variant.deleteCharAt(i);
findAllVariants(word, variant, i, q);
}
}
}
However, I end up getting a large number of copies of answers, and none of others. When I do my algorithm on paper, it seems correct. What am I doing wrong?
Something along the lines of the following code will enable you to get all possibilities (remember to add word itself if needed). The idea is to retreive all possibilities for removing one char (e.g. hello results in ello hllo helo hell). These results can in turn be used to get the possibilities for removing two chars (remove one char again). Resulting in llo elo ell for ello and so on...
List<String> getPossibilities(String word) {
int removeChars = word.length() - 1;
List<String> possibilities = new ArrayList();
List<String> options = Arrays.asList(word);
for(int i = 0; i <= removeChars; i++) {
List<String> results = new ArrayList();
for(String option : options) {
for(String result : removeOneChar(option)) {
if(!results.contains(result)) {
results.add(result);
}
}
}
possibilities.addAll(results);
options = results;
}
return possibilities;
}
private static List<String> removeOneChar(String word) {
List<String> results = new ArrayList();
for(int i = 0; i < word.length(); i++) {
int secondPart = i + 2;
if(secondPart <= word.length()) {
results.add(
word.substring(0, i)
+ word.substring(i + 1, word.length()));
}
else {
results.add(
word.substring(0, i));
}
}
return results;
}
Notice the if(!contains(result)) in order to prevent any duplicates.
Note I've used substring() to accomplish this, you're approach with removeCharAt() is a another good option. You could run some tests to see which performs better to decide which one to use. Notice using the latter could possibly remove the need of the if in the private method.
I'd use rather different algorithm: I'd find all repetitions (ll) (oo) (lll) (ooo) etc.., keep an array describing their positions in the text, and the count of characters per each repetition.
e.g Array A =
[l|2]
[o|2]
.
.
.
Then I'd say have second array with initial count zero and increase there the count and print out all permutations:
Array B =
[l|1]
[o|1]
==> prints helo
Step 2: (increment count)
B =
[l|2]
[o|1]
==> prints hello
Step 3:
B =
[l|3] ==> bigger than max,so reset it to 0, and increment the second cell now, so it becomes:
B =
[l|1]
[o|2]
==> prints heloo
Step 4: (increment first elem again)
[l|2] ==> not bigger than max, so no overflow, so keeping it that way
[o|2]
==> prints helloo