【问题标题】:Using pairs of words from a dictionary with no letters in common, find a pair that maximizes the sum of the words' lengths使用字典中没有共同字母的单词对,找到使单词长度总和最大的单词对
【发布时间】:2015-06-26 09:09:42
【问题描述】:

问题:

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

示例字典:mouse、cow、join、key、dog

dogkey 不共用字母,总和为 3+3 = 6

mouse 不适用于 cowjoindog,因为它们都共享字母 'o '

joinkey 不共用字母,总和为 4+3 = 7

我在一次采访中遇到了这个问题,我想出的解决方案概述如下。我想知道是否有任何方法可以提高效率?我使用了两个BitSets 来映射两个单词的字母表,并将它们组合在一起以查看它们是否包含相同的字母。我认为我的算法的复杂度为 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

【问题讨论】:

  • 我不知道你为什么说 o(n!)。您的算法对于蛮力方法有点低效,但它是 O(n^2 avg(length)) 其中 avg(length) 表示字典中单词的平均长度。正如 Bogdan Pop 回答的那样,通过只计算每个单词的内容一次,您可以非常轻松地提高效率。应该有比蛮力更好的方法,但是 n^2 比 n! 好很多。
  • Quora 上有同样的问题:quora.com/…
  • 谢谢!我不太确定复杂性,感谢您澄清这一点。
  • Hmmmmm,如果一个字母可能在一个单词中重复 - 它会改变最佳方法:joinkeyyyyyyyy 不共享字母,但长度大于 joinkey .

标签: java string algorithm dictionary


【解决方案1】:

您可以在 O(N^2 * 26) 中执行此操作(26 代表字典中的字符数,可能是英文字母)。

第一 构建一个二维布尔数组,D[i][j]。

i - 整数

j - character 从 'a' 到 'z' (你可以用 ASCII 码代替字符)

如果索引 i 处的单词包含字母 j,则 D[i][j] = 1,否则 D[i][j] = 0;

拥有这个二维数组后,您可以检查每对单词是否有一个共同的字母(您迭代每一对,字典中的每个字母)。如果他们不这样做,您将实现最大总和。

【讨论】:

    【解决方案2】:

    这是一种可以称为O(n*avg)的方法,尽管它可能应该称为O(n*avg+a*2^a),其中a是字母的大小,n是单词的数量,avg是单词的平均长度字典,以防您可能会使用较大的字母表。如果你的字典只有几千个单词,它不会比 Bogdan Pop 提到的 brute force 方法有所改进,但是当字典包含数十万个单词时,它会是一个改进。相比之下,一些竞争性拼字游戏玩家使用的SOWPODS dictionary 包含超过 267000 个单词。这种方法的大部分,在最后一段之前,与Michal Forišek's answer on Quora基本相同。

    对于每个单词 w,确定它的长度 l(w) 以及它包含哪些字母 s(w)。

    对于字母表的 2^a 子集中的每一个,通过遍历每个单词 w 并用其自身和 l(w) 的最大值替换 s(w) 中存储的长度来确定恰好包含这些字母的最长单词.

    对于 2^a 字母表中的每一个子集,确定最多包含这些字母的最长单词。这可以在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)。至少,在标准的英语词典中,很少使用字母,所以这会有所减少。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-04
      • 1970-01-01
      • 2011-10-02
      • 2021-05-19
      • 1970-01-01
      • 1970-01-01
      • 2012-02-02
      相关资源
      最近更新 更多