每周日早上,美国国家公共电台(NPR)的字谜大师威尔·肖特兹都会呈现字谜节目。在今天的讲座中,我们将构建一个程序来解决之前在该广播中出现的一个谜题。
在这个谜题中,我们得到了几对六个字母的单词的列表。对于每一对单词,我们必须找到一个两个字母的前缀,这样当将相同的前缀添加到这对单词中的每个单词时,得到的单词也是有效的英语单词。例如,给定一对
父母娇小的
我们可以加上前缀“ap”组成单词
明显的偏好
我准备了一个名为“problems.txt”的文本文件,其中包含了这些字谜问题的列表。我们这节课的任务是构建一个程序来解决列表中的赢博体育难题。
像许多字谜一样,这个谜题可以通过强力搜索策略来解决。具体来说,我们将这样做:
我们的解决方案策略要求我们有一个有效的八个字母单词的字典。为了使这个字典更容易使用,我在dictionary .java文件中构造了一个dictionary类。
包前缀;进口java.io.File;进口java.util.Scanner;进口java.util.Set;进口java.util.TreeSet;public class Dictionary {private Set<String> words = new TreeSet<String>();公共字典(int长度){扫描器输入= null;try {input = new Scanner(new File("words.txt"));} catch(Exception ex) {ex. printstacktrace ();返回;} while(input.hasNext()){字符串字= input.next();If (word.length() == length) {words.add(word);}} input.close();}公共布尔包含(字符串字){返回word .contains(字);}}
为什么我选择构造一个类?我有两个主要动机:
赢博体育类都有存储在成员变量中的内部数据。我们的Dictionary类将在TreeSet of Strings中存储一个有效单词列表。正如我们在前面的讲座中看到的,TreeSet类提供了一个快速的contains()方法,我们可以使用它来检查给定的String是否是一个有效的单词。反过来,这将允许我们在类中提供一个contains()方法,该方法将检查给定的String是否为有效单词。
构造类时,将有机会构造一个或多个构造函数,用它们初始化成员变量中的数据。在本例中,我们需要打开一个包含有效英语单词列表的文本文件,并将该列表中的单词放入有效单词的内部列表中。对于这个问题,我们要考虑的另一个问题是寻找给定长度的有效单词。为了解决我们的字谜,我们将只组成八个字母的单词,所以我们可以限制我们的字典中只有有效的八个字母的英语单词。构造函数接受一个length形参,可以使用该形参指定要将单词列表限制在什么长度的单词。构造函数中的主循环检查从单词列表中传入的每个单词,看它的长度是否正确。只有长度正确的单词才会进入我们的内部单词列表。此外,通过将长度作为构造函数的参数,我们可以很容易地将Dictionary类用于其他问题。在今天这堂课的作业中,我会要求你们寻找有效的六个字母的单词,而不是我们这里使用的八个字母的单词。
Dictionary类需要使用一个英语单词列表。我使用了我在websites.umich.edu/~jlawler/wordlist.html网站上找到的单词列表。
这是解谜程序的代码。
包前缀;进口java.io.File;进口java.util.ArrayList;进口并不知道;进口java.util.Scanner;公共类前缀{私有静态字典;public static void main(String[] args) {dictionary = new dictionary (8);List<String> problems = new ArrayList<String>();扫描器输入= null;try {input = new Scanner(new File("problems.txt"));} catch(Exception ex) {ex. printstacktrace ();返回;} while(input.hasNext()) {String first = input.next();字符串秒= input.next();solveProblem(一、二);}}公共静态void solveProblem(字符串word1,字符串word2) {System.out。println(“正在处理” + word1 + “和” + word2 + ":");char A[] = new char[2];(char ch1 = a; ch1 < =“z”;ch1 + +) {[0] = ch1;(char ch2 = a; ch2 < =“z”;ch2 + +) {[1] = ch2;字符串前缀=新字符串(A);字符串fullWord1 =前缀+ word1;字符串fullWord2 = prefix + word2;if(dictionary.contains(fullWord1) && dictionary.contains(fullWord2)) {System.out.println(fullWord1);System.out.println (fullWord2);返回;}}} System.out。println(“没有找到解决方案”);}}
这里的主程序处理设置字典和从文本文件中读取单个问题的后勤工作。
实际的解谜逻辑被分解成第二个方法,solveProblem()。
我需要解决的一个小问题是,如何通过在两个单词前加上两个字母的前缀来形成新的字符串。如果可以将两个字母前缀组成一个字符串,则可以使用熟悉的string +操作符将该前缀与单词连接起来。我知道我要做的另一件事是使用循环结构循环遍历赢博体育可能的两个字母前缀,就像这样:
for(char ch1 = 'a';ch1 <= 'z';ch1++) {for(char ch2 = 'a';ch2 <= 'z';ch2++){//从ch1和ch2中生成一个前缀字符串}}
我的问题的解决方案是查看String类文档中String类的赢博体育可能构造函数的列表。其中一个构造函数允许我们从字符数组初始化String对象。考虑到这一点,我使用这个逻辑来构造前缀字符串,并将它与谜题中的两个单词连接起来:
char A[] = new char[2];(char ch1 = a; ch1 < =“z”;ch1 + +) {[0] = ch1;(char ch2 = a; ch2 < =“z”;ch2 + +) {[1] = ch2;字符串前缀=新字符串(A);字符串fullWord1 =前缀+ word1;字符串fullWord2 = prefix + word2;//现在搜索字典,看看这两个词是否都是有效的。//如果是,打印解决方案并返回}}
除了编写程序来解决字谜之外,我们还可以编写程序来生成字谜。在这个作业中,你将编写一个程序来生成一个更大的列表,这些列表是我们刚刚解决的相同类型的单词问题的示例。
具体来说,您将编写一个程序来完成以下工作:
以下是我在解决这个问题时发现的一些群体的例子:
令人震惊的户外全能的虽然利他主义明显的胃口评估批准灯丝炉边拾荒者嘲笑侦察乱爬乱爬尖叫洗涤者
你应该能找到更多的词组。
这里有一个有用的提示,可以使您的解决方案运行得更快:利用您拥有的单词列表是按字母顺序排列的这一事实。