首先要注意的是,如果您通过从 5 行中的每一行中选择 3 个字符中的一个来造词,那么您总共会得到 35 = 243 个单词。无论您如何实施该程序,它最终都必须构建这 243 个单词中的每一个。
递归是一种很好的实施策略,因为它清楚地表明您要选择第一行中的三个字符中的一个,并且对于每个选择,您继续选择第二行中的三个字符中的一个,等等。
在下面的 Java 程序中,makeWord 的第一个版本是一个递归函数,它在由currentRowIndex 索引的行中选择一个字符并将该字符附加到wordBuffer。如果这是最后一行,则该单词是完整的并且它被附加到单词列表中。否则,该函数会调用自身以在行 currentRowIndex + 1 上工作。
请注意,wordBuffer 的当前状态会传递到递归调用。只有从递归调用返回后,我们才会删除wordBuffer 中的最后一个字符。
makeWord 的第二个版本允许您传递行索引数组,用于指定要从哪些行中选择字符。例如,要从第 1、3 和 6 行中选择字符,您可以调用:
permuter.makeWord(new int[]{ 1, 3, 6 }, 0);
您可以在 main 方法中替换该调用而不是当前行,这会导致使用第 1 行到第 5 行的字符构建单词:
permuter.makeWord(1, 5);
如果您仔细查看makeWord 方法,您会发现第一个在字符串完成时不会递归,而第二个因为position == indices.length 而递归一次然后提前返回。后一种方法的效率略低,因为它需要更多的递归调用,但您可能会发现它更清楚地表达了递归的概念。这是一个品味问题。
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();
}
}