问题:
使用字典中没有共同字母的单词对,找到 最大化单词总和的一对'长度
示例词典:mouse,cow,join,key,dog
狗和键不分享任何字母,总和为3 + 3 = 6
鼠标不能与牛,加入或狗一起使用,因为它们都共享字母&# 39;○'
加入和密钥不分享任何字母,总和为4 + 3 = 7
我在接受采访时遇到了这个问题,我提出的解决方案概述如下。我想知道是否有任何方法可以提高效率?我使用两个BitSets
来映射两个单词的字母和AND它们在一起以查看它们是否包含相同的字母。我认为我的算法的复杂度是o(n!)这是低效的,是否有更好的方法来优化我的算法?
public static void maximumSum (String[] dictionary) {
// ascii of a = 97
BitSet word1 = new BitSet(26);
BitSet word2 = new BitSet(26);
String maxWord1 = "";
String maxWord2 = "";
int maxSum = -1;
for(int i = 0; i<dictionary.length; i++) {
for(int j = i+1; j<dictionary.length; j++) {
String s1 = dictionary[i];
String s2 = dictionary[j];
for(int k = 0; k<s1.length(); k++) {
word1.set(s1.charAt(k)-97);
}
for(int k = 0; k<s2.length(); k++) {
word2.set(s2.charAt(k)-97);
}
word1.and(word2);
if(word1.cardinality() == 0) {
if(maxSum < s1.length()+s2.length()) {
maxWord1 = s1;
maxWord2 = s2;
maxSum = s1.length()+s2.length();
}
}
word1.clear();
word2.clear();
}
}
if(maxSum == -1)
System.out.println("All the words have letters in common.");
else
System.out.println("'"+maxWord1+"' and '"+maxWord2+"'
have a maximum sum of "+maxSum);
}
public static void main(String[] args) {
String[] dictionary = {"mouse", "cow", "join", "key", "dog"};
maximumSum(dictionary);
}
输出:
'join' and 'key' have a maximum sum of 7
答案 0 :(得分:0)
您可以在O(N ^ 2 * 26)中执行此操作(26表示字典中的字符数,可能是英文字母)。
<强>第一强> 构建一个2D bool数组,D [i] [j]。
i - 整数
j - 字符来自&#39; a&#39;到&#39; z&#39; (您可以使用ASCII代码而不是字符)
如果索引i处的单词包含字母j,则D [i] [j] = 1,否则D [i] [j] = 0;
拥有这个2D数组之后,如果它们有一个共同的字母,你可以检查每对单词(你迭代每一对,你字典的每个字母)。如果他们没有,你就会实现最大金额。
答案 1 :(得分:0)
这是一种可以称为O(n*avg)
的方法,但也许它应该被称为O(n*avg+a*2^a)
,其中a是字母表的大小,n是单词的数量,avg是平均长度字典中的单词,如果您可以使用更大的字母表来执行此操作。如果你的字典只有几千个单词,那么它不会比Bogdan Pop提到的强力方法有所改进,但当字典包含数十万个单词时,这将是一个改进。相比之下,一些竞争性Scrabble玩家使用的SOWPODS dictionary包含超过267000个单词。在最后一段之前,这种方法的大多数与Michal Forišek's answer on Quora基本相同。
对于每个单词w,确定其长度l(w)以及它包含的字母s(w)。
对于字母表中的每个子集,通过迭代每个单词w确定包含这些字母的最长单词,并将s(w)中存储的长度替换为其自身的最大值和l(w)
对于字母表中的每个子集,确定最多包含这些字母的最长单词。这可以在O(a*2^a)
步骤中完成,因为大小为k的每个子集覆盖k个具有k-1个字母的较小子集。我们将这个值归纳为最长的单词,它包含那些字母或包含在其中一个k-1覆盖的子集中。
现在我们可以迭代字母表的所有子集,使用这些字母加上最长单词的长度,以及该子集的补充中包含的最长单词的长度。
这种方法的一个令人不满意的部分是,由于包含了不常见的字母,时间增加了很多。由于Q,X,J和Z,所花费的时间大约增加了20倍,这在一般的单词中并不常见,我认为也不是长话。因此,假设有r个单词至少包含b个罕见字母中的一个。我们可以在O(n*avg+(a-b)2^(a-b))
步骤中对没有稀有字母的单词使用上述算法。然后我们可以在O(r^2*a)
步骤中强制使用包含稀有字母的单词对。最后,我们可以检查第一个包含稀有字母的单词对,但第二个不是通过迭代包含稀有字母的r个单词,检查补充中只有普通字母的最长单词。最后一种情况需要O(r*avg)
步,因此渐近可忽略不计。因此,通过选择一些罕见字母,我们可以将时间缩短到O(n*avg+(a-b)2^(a-b)+r^2*avg)
。至少,使用标准的英语词典,很少使用字母,所以这将是一种减少。