[英]Regex match if all characters in a dictionary word are present in the phrase. The number of times each character occurs must also match in each other
我正在编写递归回溯搜索来查找短语的字谜。 第一步,我试图在将字典提供给递归算法之前从字典中过滤掉所有错误的单词。
字典文件如下所示:
aback
abacus
abalone
abandon
abase
...
[40,000 more words]
我要构建的正则表达式必须过滤掉包含短语不包含的字符的单词,以及包含比短语中存在的字符更多的单词。
例如,给定短语“clint eastwood”,单词“noodle”匹配,但单词“stonewall”不匹配,因为“stonewall”包含的“l”字符比“clint eastwood”包含的字符多。
简单地使用"[clint eastwood]+"
作为正则表达式几乎可以满足我的要求,但它包含短语中包含任意数量字符的单词。
正则表达式是比较字符数的错误工具。 任何满足此要求的正则表达式都可能很笨拙且效率极低。 遍历每个单词并跟踪单个字符数会更好。
无论如何,这里有一种构造匹配“错误单词”的正则表达式的方法(反过来更难):首先,从短语中包含的一组不同字符{a1,...,aN}
中,您可以匹配包含任何非法字符的所有单词[^a1,...,aN]
。 然后,对于在目标字符串中出现n
次的每个字符c
,构建一个子表达式(.*c.*){n+1}
,然后将这些片段与|
. 对于clint eastwood
,你应该得到:
(.*c.*){2}|(.*l.*){2}|(.*i.*){2}|(.*n.*){2}|(.*t.*){3}|(.*e.*){2}|(.*a.*){2}|(.*s.*){2}|(.*w.*){2}|(.*o.*){3}|(.*d.*){2}|[^clinteaswod]
如上一个答案所述,正则表达式不是您应该查看的内容。 您需要记录每个单词的字符数,以便稍后快速过滤无效行。 我有一个使用Map<String, Map<Character, Integer>>
的解决方案。
Map<String, Map<Character, Integer>> wordCharacterCount = new HashMap<>();
try (Scanner scanner = new Scanner(new File(...))) {
while (scanner.hasNextLine()) {
String word = scanner.nextLine();
Map<Character, Integer> characterCount = new HashMap<>();
char[] characters = word.toCharArray();
for (int i = 0; i < characters.length; i++) {
char c = Character.toLowerCase(characters[i]);
if (Character.isLetter(c)) {
if (!characterCount.containsKey(c)) {
characterCount.put(c, 1);
} else {
characterCount.put(c, characterCount.get(c) + 1);
}
}
}
wordCharacterCount.put(word, characterCount);
}
}
为简单起见,我使用了 Stream API。 对于您想要过滤字典条目的每个短语,您构造一个类似的 Map<Character, Integer> 并遍历 Map 以过滤条目,具体取决于它是否 (1) 包含无效字符或 (2) 具有更大的字符数比提供的短语。
String testWord = "clint eastwood";
char[] characters = testWord.toCharArray();
for (int i = 0; i < characters.length; i++) {
char c = Character.toLowerCase(characters[i]);
if (Character.isLetter(c)) {
if (!testWordCharacterCount.containsKey(c)) {
testWordCharacterCount.put(c, 1);
} else {
testWordCharacterCount.put(c, testWordCharacterCount.get(c) + 1);
}
}
}
List<String> validWords = wordCharacterCount.keySet().stream()
.filter(word -> {
Map<Character, Integer> currentWordCharacterCount = wordCharacterCount.get(word);
for (Entry<Character, Integer> entry : currentWordCharacterCount.entrySet()) {
char c = entry.getKey();
int count = entry.getValue();
if (!testWordCharacterCount.containsKey(c) || testWordCharacterCount.get(c) < count) {
return false;
}
}
return true;
}).collect(Collectors.toList());
我没有对此进行彻底的基准测试,但在我的使用中,使用包含 460,000 个条目的字典,预处理需要大约 600 毫秒,过滤器每个需要大约 50-150 毫秒。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.