【发布时间】:2017-07-19 17:42:35
【问题描述】:
我正在尝试解决古老的字谜问题。感谢那里的许多教程,我能够遍历一组字符串,递归地找到所有排列,然后将它们与英语单词列表进行比较。我发现的问题是,在大约三个词之后(通常是“变形”之类的词),我得到了 OutOfMemory 错误。我尝试将我的批次分成小组,因为它似乎是消耗我所有内存的递归部分。但即使只是“变形”也将其锁定...
在这里,我将文件中的单词读入列表
Scanner scanner = new Scanner(resource.getInputStream());
while (scanner.hasNext()) {
String s = scanner.nextLine();
uniqueWords.add(s.toLowerCase());
}
现在我将它们分成更小的集合并调用一个类来生成字谜:
List<List<String>> subSets = Lists.partition(new ArrayList(uniqueWords), SET_SIZE);
for (List<String> set: subSets) {
// tried created as class attribute & injection, no difference
AnagramGenerator anagramGenerator = new AnagramGenerator();
List<Word> anagrams = anagramGenerator.createWordList(set);
wordsRepository.save(anagrams);
LOGGER.info("Inserted {} records into the database", anagrams.size());
}
最后是我的生成器:
public class AnagramGenerator {
private Map<String, List<String>> map = new Hashtable<>();
public List<Word> createWordList(List<String> dictionary) {
buildAnagrams(dictionary);
List<Word> words = new ArrayList<>();
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
words.add(new Word(entry.getKey(), entry.getValue()));
}
return words;
}
private Map<String, List<String>> buildAnagrams(List<String> dictionary) {
for (String str : dictionary) {
String key = sortString(str);
if (map.get(key) != null) {
map.get(key).add(str.toLowerCase());
} else {
if (str.length() < 2) {
map.put(key, new ArrayList<>());
} else {
Set<String> permutations = permutations(str);
Set<String> anagramList = new HashSet<>();
for (String temp : permutations) {
if (dictionary.contains(temp) && !temp.equalsIgnoreCase(str)) {
anagramList.add(temp);
}
}
map.put(key, new ArrayList<>(anagramList));
}
}
}
return map;
}
private Set<String> permutations(String str) {
if (str.isEmpty()) {
return Collections.singleton(str);
} else {
Set<String> set = new HashSet<>();
for (int i = 0; i < str.length(); i++)
for (String s : permutations(str.substring(0, i) + str.substring(i + 1)))
set.add(str.charAt(i) + s);
return set;
}
}
编辑: 根据出色的反馈,我已将生成器从排列更改为工作查找:
public class AnagramGenerator {
private Map<String, Set<String>> groupedByAnagram = new HashMap<String, Set<String>>();
private Set<String> dictionary;
public AnagramGenerator(Set<String> dictionary) {
this.dictionary = dictionary;
}
public List<Word> searchAlphabetically() {
List<Word> words = new ArrayList<>();
for (String word : dictionary) {
String key = sortString(word);
if (!groupedByAnagram.containsKey(key)) {
groupedByAnagram.put(key, new HashSet<>());
}
if (!word.equalsIgnoreCase(key)) {
groupedByAnagram.get(key).add(word);
}
}
for (Map.Entry<String, Set<String>> entry : groupedByAnagram.entrySet()) {
words.add(new Word(entry.getKey(), new ArrayList(entry.getValue())));
}
return words;
}
private String sortString(String goodString) {
char[] letters = goodString.toLowerCase().toCharArray();
Arrays.sort(letters);
return new String(letters);
}
它有更多的调整,所以我没有添加一个单词,因为它是自己的字谜,但除此之外,这似乎非常快。而且,代码更干净。谢谢大家!
【问题讨论】:
-
你在哪里得到错误?堆栈跟踪?
-
你在那里创造了一大堆集合..
-
使用递归查找排列需要大量开销,并且通常涉及为您的程序增加分配的堆空间。我建议使用另一种方式来创建所有排列。
-
我同意我正在使用递归创建大量 Set,但到目前为止,这是进行字符串操作的唯一方法。任何替代的想法?可以像 char[] 交换器一样简单吗?
-
注意如果你有重复的字母,你会得到更少的组合。例如
add有 3 个字谜,但the有 6 个。