基本思路就是对棋盘的深度优先搜索,并且根据单词表的前缀剪枝。即如果某个前缀在单词表中不存在,则此停止搜索。求解时间在1.5s左右。
单词表来自:http://www.freebsd.org/cgi/cvsweb.cgi/src/share/dict/web2?rev=1.12;content-type=text%2Fplain
单词索引利用Trie实现。
Boggle主程序:
import java.util.*;
import java.io.*;
import java.nio.charset.Charset;
public class Boggle {
private final static char board[][] = {
{'f', 'x', 'i', 'e'},
{'a', 'm', 'l', 'o'},
{'e', 'w', 'b', 'x'},
{'a', 's', 't', 'u'}
};
public static void searchAll(char[][] board, boolean[][] visited, int i, int j, String s, List list, Trie trie) {
if (i >= board.length || j >= board.length || i < 0 || j < 0) {
return;
}
if (visited[i][j]) {
return;
}
String ss = s + board[i][j];
String ssU = s + Character.toUpperCase(board[i][j]);
if (ss.length() > 2) {
if (trie.contains(ss)) {
list.add(ss);
}
}
if (ssU.length() > 2) {
if (trie.contains(ssU)) {
list.add(ssU);
}
}
for (int ii = -1; ii < 2; ii++) {
for (int jj = -1; jj < 2; jj++) {
if (ii != 0 || jj != 0) {
visited[i][j] = true;
if (trie.isPrefix(ss)) {
searchAll(board, visited, i + ii, j + jj, ss, list, trie);
}
if (trie.isPrefix(ssU)) {
searchAll(board, visited, i + ii, j + jj, ssU, list, trie);
}
visited[i][j] = false;
}
}
}
}
public static Iterable boggle() {
Trie trie = buildTrie("web2");
List list = new LinkedList();
boolean visited[][] = {
{false, false, false, false},
{false, false, false, false},
{false, false, false, false},
{false, false, false, false}
};
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board.length; j++) {
searchAll(board, visited, i, j, "", list, trie);
}
}
return list;
}
public static Trie buildTrie(String filename) {
Trie trie = new Trie();
int cnt = 0;
InputStream fis = null;
BufferedReader br = null;
String line = null;
try {
fis = new FileInputStream(filename);
br = new BufferedReader(new InputStreamReader(fis, Charset.forName("UTF-8")));
while ((line = br.readLine()) != null) {
trie.put(line);
cnt++;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (Exception e) {}
}
if (br != null) {
try {
br.close();
} catch (Exception e) {}
}
}
/* System.out.println("# of lines: " + cnt);
* System.out.println("# of words: " + trie.size()); */
return trie;
}
public static void main(String[] args) {
long startTime = System.nanoTime();
Iterable list = boggle();
long consumingTime = System.nanoTime() - startTime;
Iterator it = list.iterator();
int cnt = 0;
while (it.hasNext()) {
cnt++;
System.out.println(it.next());
}
System.out.println("Found " + cnt + " words in " + consumingTime / 1000000000.0 + " s.");
}
}
import java.util.*;
public class Trie {
private static final int RADIX = 256;
class Node {
Node[] next = new Node[RADIX];
boolean isWord = false;
}
private Node root = null;
public int size() {
return size(root);
}
private int size(Node x) {
if (x == null) {
return 0;
}
int cnt = 0;
if (x.isWord) {
cnt = 1;
}
for (int i = 0; i < RADIX; i++) {
cnt += size(x.next[i]);
}
return cnt;
}
public void put(String word) {
root = put(root, word, 0);
}
private Node put(Node x, String word, int d) {
if (x == null) {
x = new Node();
}
if (word.length() == d) {
x.isWord = true;
return x;
}
char c = word.charAt(d);
x.next[c] = put(x.next[c], word, d + 1);
return x;
}
public boolean contains(String word) {
return contains(root, word, 0);
}
private boolean contains(Node x, String word, int d) {
if (x == null) {
return false;
}
if (word.length() < d) {
return false;
}
if (word.length() == d) {
return x.isWord;
}
char c = word.charAt(d);
return contains(x.next[c], word, d + 1);
}
public boolean isPrefix(String prefix) {
return isPrefix(root, prefix, 0);
}
private boolean isPrefix(Node x, String prefix, int d) {
if (x == null) {
return false;
}
if (prefix.length() <= d) {
return true;
}
char c = prefix.charAt(d);
return isPrefix(x.next[c], prefix, d + 1);
}
private Node getPrefixNode(Node x, String prefix, int d) {
if (x == null) {
return null;
}
if (prefix.length() <= d) {
return x;
}
char c = prefix.charAt(d);
return getPrefixNode(x.next[c], prefix, d + 1);
}
private boolean nodeContainsKey(Node x) {
if (x == null) {
return false;
}
if (x.isWord) {
return true;
}
boolean res = false;
for (int i = 0; i < RADIX; i++) {
res = (res || nodeContainsKey(x.next[i]));
}
return res;
}
public Iterable getAll() {
List list = new LinkedList();
getAll(root, list, "");
return list;
}
private void getAll(Node x, List list, String s) {
if (x == null) {
return;
}
if (x.isWord == true) {
list.add(s);
}
for (int i = 0; i < RADIX; i++) {
getAll(x.next[i], list, s + (char) i);
}
}
}