在一组多维数据集中查找单词的代码的复杂性是多少

时间:2015-04-13 22:15:48

标签: algorithm time-complexity complexity-theory string-matching asymptotic-complexity

我已经解决了here计划。以前我认为复杂性是O(n!) 其中n是单词中的字符。

但今天我觉得这是错的。它应该是(6)^(单词中的字符),其中6是立方体中的边。

使它更通用,假设cube有6个以上的边,复杂度应该是O(cubefaces ^(输入字中的字符))

有人可以在这种情况下向我解释时间复杂度吗?

2 个答案:

答案 0 :(得分:5)

如果有if (cubeMatrix.length != word.length()) return false;,并且立方体的每个侧面字母都是唯一的(即多维数据集的两边没有相同的字母),那么算法的时间复杂度为 O S N - S + 1 S !) N时,N > = S O S N !) < = S 。这里 S 是立方体边数, N 是立方体数。

简而言之,只有在对应于立方体边字母的单词中有一个未使用的字母时才进行递归调用,因此,在最坏的情况下,进行递归调用的次数不超过字母未使用。并且未使用的单词字母的数量随着递归深度的增加而减少,并且最终该数字变得小于立方体边的数量。这就是为什么在最终的递归深度中,复杂性变成了因子。

更多细节

让我们介绍 f n ),这是您使用findWordExists = n调用cubeNumber的次数。我们还介绍 g n ),这是findWordExists cubeNumber = n 递归调用自身的次数(但现在cubeNumber = n + 1)。

f (0)= 1,因为您只能非递归地调用findWordExists一次。

f n )= f n - 1) g n - 1)当 n > 0

我们知道 g n )= min { S N - n },因为正如我已经指出的那样,递归调用findWordExists的次数不会超过剩余的字母数 - if (frequency > 0)检查对此负责 - 并且剩下的字母数等于剩下的立方体数,即 N - n

现在我们可以计算总共调用findWordExists的次数:
f (0)+ f (1)+ ... + f N )=
= 1 + g (0)+ g (0) g (1)+ ... + g (0) g (1)... g N - 1)=
= 1 + S + S 2 + ... + S N - S + S N - S (< em> S - 1)+ S N - S S - 1)( S - 2)+ ... + S N - S S - 1)( S - 2)... 1 =
= O S N - S S !)。

但每次findWordExists调用(除了决赛)都会遍历每一方,因此我们需要将findWordExists次调用的次数乘以边数: S O S N - S S !)= O S N - S + 1 S !) - 这就是我们的时间复杂性。

更好的算法

实际上,你的问题是一个二分匹配问题,因此有比蛮力更有效的算法,例如: Kuhn’s algorithm

Kuhn算法的复杂性是 O N M ),其中 N 是顶点的数量, M 是边数。在您的情况下, N 是多维数据集的数量, M 只是 N 2 ,因此复杂性在你的情况可能是 O N 3 )。但是你还需要迭代所有立方体的所有边,所以如果立方体边的数量大于 N 2 ,那么复杂性是 O N S ),其中 S 是立方体边数。

这是一个可能的实现:

import java.util.*;

public class CubeFind {
    private static boolean checkWord(char[][] cubes, String word) {
        if (word.length() != cubes.length) {
            return false;
        }
        List<Integer>[] cubeLetters = getCubeLetters(cubes, word);
        int countMatched = new BipartiteMatcher().match(cubeLetters, word.length());
        return countMatched == word.length();
    }

    private static List<Integer>[] getCubeLetters(char[][] cubes, String word) {
        int cubeCount = cubes.length;

        Set<Character>[] cubeLetterSet = new Set[cubeCount];
        for (int i = 0; i < cubeCount; i++) {
            cubeLetterSet[i] = new HashSet<>();
            for (int j = 0; j < cubes[i].length; j++) {
                cubeLetterSet[i].add(cubes[i][j]);
            }
        }
        List<Integer>[] cubeLetters = new List[cubeCount];
        for (int i = 0; i < cubeCount; i++) {
            cubeLetters[i] = new ArrayList<>();
            for (int j = 0; j < word.length(); j++) {
                if (cubeLetterSet[i].contains(word.charAt(j))) {
                    cubeLetters[i].add(j);
                }
            }
        }
        return cubeLetters;
    }

    public static void main(String[] args) {
        char[][] m = {{'e', 'a', 'l'} , {'x', 'h' , 'y'},  {'p' , 'q', 'l'}, {'l', 'h', 'e'}};
        System.out.println("Expected true,  Actual: " + CubeFind.checkWord(m, "hell"));
        System.out.println("Expected true,  Actual: " + CubeFind.checkWord(m, "help"));
        System.out.println("Expected false, Actual: " + CubeFind.checkWord(m, "hplp"));
        System.out.println("Expected false, Actual: " + CubeFind.checkWord(m, "hplp"));
        System.out.println("Expected false, Actual: " + CubeFind.checkWord(m, "helll"));
        System.out.println("Expected false, Actual: " + CubeFind.checkWord(m, "hel"));
    }
}

class BipartiteMatcher {
    private List<Integer>[] cubeLetters;
    private int[] letterCube;
    private boolean[] used;

    int match(List<Integer>[] cubeLetters, int letterCount) {
        this.cubeLetters = cubeLetters;
        int cubeCount = cubeLetters.length;
        int countMatched = 0;

        letterCube = new int[letterCount];
        Arrays.fill(letterCube, -1);

        used = new boolean[cubeCount];
        for (int u = 0; u < cubeCount; u++) {
            if (dfs(u)) {
                countMatched++;

                Arrays.fill(used, false);
            }
        }
        return countMatched;
    }

    boolean dfs(int u) {
        if (used[u]) {
            return false;
        }
        used[u] = true;

        for (int i = 0; i < cubeLetters[u].size(); i++) {
            int v = cubeLetters[u].get(i);

            if (letterCube[v] == -1 || dfs(letterCube[v])) {
                letterCube[v] = u;
                return true;
            }
        }
        return false;
    }
}

答案 1 :(得分:2)

for (int i = 0; i <  cubeMatrix[cubeNumber].length; i++) 

这将告诉给定立方体(或多维数据集的面)中的字符数。

此外,在此循环中,您有一个

if (frequency > 0) {
   charFreq.put(cubeMatrix[cubeNumber][i], frequency - 1);
   if (findWordExists(cubeMatrix, charFreq, cubeNumber + 1)) {
      return true;
          ..
          // and so on.

这将导致递归调用,从而调用cubeNumber + 1,然后调用cubeNumber + 1 + 1,..等等。

而且,最后这个条件

if (cubeNumber == cubeMatrix.length) {
        for (Integer frequency : charFreq.values()) {
            if (frequency > 0) return false;
        }
        return true;
    }

会见,for-loop将不再执行。

假设,没有。 cubes = n,存储在每个立方体中的字符=每个立方体的通用面(由OP创造)= f。

最糟糕的案例分析: -

从第0个立方体到第(n-1)个立方体,for循环将迭代cubeMatrix [cubeNumber] .length次数,它等于每个立方体中存储的字符数= f次。

并且,在for循环的每次迭代中,在cubeNumber 0的情况下,实际的递归调用将是n-1次,直到它到达最后一个多维数据集编号。

因此,对于cubeArray中的每个字符条目(f个字符),我们必须调用所有可用的多维数据集(根据我们的假设,总数为n)。

因此,代码检查查找单词的总次数= f ^ n。

在你的术语中,f = cubefaces =广义立方体面上可能出现的字符数;

和,n =可用于测试的立方体总数。

  

它取决于基于减少的字符的频率   当单词长度与单词长度不匹配时单词中的字符   立方体的数量。在这种情况下,结果将为false。

     

但是,在字长等于立方体数的情况下,   在最坏的情况下,输出将与字长无关。

严格来说,它还取决于单词的字符数(与频率比较会减少计算中的几种情况),但是,在最坏的情况下,不幸的是,它不依赖于字符数在单词 中,因为我们将检查所有可用多维数据集中的所有字符条目 以创建单词。