使用字典中没有共同字母的单词对,找到一对最大化单词长度总和的对

时间:2015-04-19 18:57:13

标签: java string algorithm dictionary

问题:

  

使用字典中没有共同字母的单词对,找到   最大化单词总和的一对'长度

     

示例词典: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

2 个答案:

答案 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)。至少,使用标准的英语词典,很少使用字母,所以这将是一种减少。