I was asked to help solve the following word puzzle: find valid n-characters-word from m characters.
In the following example, The answer is combine.
I am not good at this kind of game. But I am a programmer, so I can write a program to do it.
I use open source English dictionary libraries:
WordNet http://wordnet.princeton.edu/wordnet/download/.
JWI: http://projects.csail.mit.edu/jwi/.
JWI is a Java library for interfacing with Wordnet.
First I will load all English words into memory, later, when we call dict.getIndexWord, it will directly search the word HashMap in the memory.
private void init(String dictFile) throws IOException, InterruptedException {
dict = new RAMDictionary(new File(dictFile), ILoadPolicy.NO_LOAD);
dict.open();
dict.load(true);
} In previous post, I explained how to solve it with recursion, and the disadvantage of it: requires a lot of memory.In this article, I solve this problem iteratively.
The main difference is that:
1. Get all combinations of n-characters-word from m characters: order is not important.
2. For every n-characters-word returned in step 1, using counting quickPerm algorithm to iterate each word in its permutations, if it is a valid word, save it to a list.
The code is like below:
public ListgetAllValidWords(String str, int len) { List result = new ArrayList<>(); List nStrs = getNChars(str, len); for (String aStr : nStrs) { result.addAll(getValidWords(aStr)); } return result; }
Another similar word game is to find the longest words from m characters.
To solve this, I call getAllValidWords(str, n), getAllValidWords(str, n-1) etc in multiple threads.
Implementation
The code is like below: you can review the complete code at Github.
The output is like below:
getAllValidWords took 755 mill seconds, found [combine, newcomb]
getMaxLenValidWord took 17601, len: 8 found [combine, newcomb]
package org.codeexample.algorithms.collected.wordpuzzle;
import edu.mit.jwi.RAMDictionary;
import edu.mit.jwi.data.ILoadPolicy;
import edu.mit.jwi.item.IIndexWord;
import edu.mit.jwi.item.POS;
public class WordPuzzleIteratively {
private RAMDictionary dict;
public WordPuzzleIteratively(String dictFile) throws IOException,
InterruptedException {
init(dictFile);
}
private void init(String dictFile) throws IOException, InterruptedException {
dict = new RAMDictionary(new File(dictFile), ILoadPolicy.NO_LOAD);
dict.open();
dict.load(true);
}
public void close() {
dict.close();
}
public List<String> getAllValidWords(String str, int len) {
long start = new Date().getTime();
List<String> result = new ArrayList<>();
List<String> nStrs = getNChars(str, len);
for (String aStr : nStrs) {
result.addAll(getValidWords(aStr));
}
System.out.println("getAllValidWords took "
+ (new Date().getTime() - start) + " mill seconds, found " + result);
return result;
}
public List<String> getMaxLenValidWordThreaded(final String str)
throws MalformedURLException, IOException, InterruptedException {
final ArrayList<ArrayList<String>> list = new ArrayList<ArrayList<String>>();
if (str == null || str.length() == 0)
return new ArrayList<>();
long start = new Date().getTime();
for (int len = str.length(); len > 0; len--) {
list.add(new ArrayList<String>());
}
ExecutorService executor = Executors.newFixedThreadPool(str.length());
for (int len = str.length(); len > 0; len--) {
final int length = len;
Runnable task = new Runnable() {
@Override
public void run() {
long innerStart = new Date().getTime();
ArrayList<String> result = (ArrayList<String>) getAllValidWords(str,
length);
System.out.println("getMaxLenValidWordThreaded len: " + length
+ " took " + (new Date().getTime() - innerStart) + ", found "
+ result.size() + " : " + result);
list.set(length, result);
}
};
executor.submit(task);
}
executor.shutdown();
boolean terminated = executor.awaitTermination(Long.MAX_VALUE,
TimeUnit.SECONDS);
if (!terminated) {
throw new RuntimeException("Request takes too much time");
}
List<String> result = new ArrayList<>();
int index = str.length() - 1;
for (; index >= 0; index--) {
result = list.get(index);
if (!result.isEmpty())
break;
}
System.out.println("getMaxLenValidWord took "
+ (new Date().getTime() - start) + ", len: " + (index + 1) + " found "
+ result);
return result;
}
public List<String> getMaxLenValidWord(String str)
throws MalformedURLException, IOException {
long start = new Date().getTime();
List<String> list = new ArrayList<>();
for (int len = str.length(); len > 0; len--) {
long innerStart = new Date().getTime();
list = getAllValidWords(str, len);
System.out.println("getMaxLenValidWord len: " + len + " took "
+ (new Date().getTime() - innerStart));
if (!list.isEmpty()) {
break;
}
}
System.out.println("getMaxLenValidWord took "
+ (new Date().getTime() - start));
return list;
}
/**
* Idea comes from book: Cracking code interview.
*/
private List<String> getNChars(String strs, int len) {
// for each char, it can be in the response or not, if
List<String> nStrs = new ArrayList<String>();
long max = 1 << strs.length();
for (int i = 0; i < max; i++) {
StringBuilder sb = new StringBuilder(len);
int k = i;
int index = 0;
while (k > 0) {
if ((k & 1) > 0) {
sb.append(strs.charAt(index));
}
k >>= 1;
index++;
}
if (sb.length() == len) {
nStrs.add(sb.toString());
}
}
return nStrs;
}
/**
* Ideas comes from: The Counting QuickPerm Algorithm:
* http://www.freewebs.com/permute/quickperm.html<br>
* http://stackoverflow.com/questions/11915026/permutations-of-a-string-using-
* iteration
*/
private List<String> getValidWords(String str) {
List<String> validWords = new ArrayList<>();
if (isValidWorld(str)) {
validWords.add(str);
}
char[] a = str.toCharArray();
int n = a.length;
int[] p = new int[n]; // Index control array initially all zeros
int i = 1;
while (i < n) {
if (p[i] < i) {
int j = ((i % 2) == 0) ? 0 : p[i];
swap(a, i, j);
String word = toString(a);
if (isValidWorld(word)) {
validWords.add(word);
}
p[i]++;
i = 1;
} else {
p[i] = 0;
i++;
}
}
return validWords;
}
private static String toString(char[] a) {
StringBuilder builder = new StringBuilder();
builder.append(a);
return builder.toString();
}
private static void swap(char[] a, int i, int j) {
char temp = a[i];
a[i] = a[j];
a[j] = temp;
}
public static void main(String[] args) throws Exception {
String str = "oivmwujbecn";
int len = 7;
WordPuzzleIteratively instance = new WordPuzzleIteratively(
"C:/Program Files (x86)/WordNet/2.1/dict");
List<String> list = null;
// getAllValidWords took 755 mill seconds, found [combine, newcomb]
list = instance.getAllValidWords(str, len);
System.out.println(list.size());
System.out.println(list);
// getMaxLenValidWord took 17601, len: 8 found [combine, newcomb]
list = instance.getMaxLenValidWordThreaded(str);
System.out.println(list.size());
System.out.println(list);
instance.close();
}
private boolean isValidWorld(String word) {
Boolean isWord = false;
IIndexWord idxWord = dict.getIndexWord(word, POS.NOUN);
if (idxWord != null && !idxWord.getWordIDs().isEmpty()) {
isWord = true;
}
return isWord;
}
}
