如果有if (cubeMatrix.length != word.length()) return false;,并且立方体的每个边的字母都是唯一的(即立方体的两侧没有相同的字母),那么你的算法的时间复杂度是O( SN - S + 1S!) 当 N >= S, O(S N!) 当N S。这里S是立方体的边数,N是立方体的个数。
简而言之,您进行递归调用只有在与立方体侧面字母对应的单词中有一个未使用的字母,因此,在最坏的情况下,您进行递归调用的次数不超过单词字母未使用。并且未使用的单词字母的数量随着递归深度的增加而减少,最终这个数字变得小于立方体的边数。这就是为什么在最终的递归深度中,复杂性变成了阶乘。
更多细节
让我们介绍一下f(n),即您使用cubeNumber = n 调用findWordExists 的次数。我们还引入了 g(n),即 findWordExists 和 cubeNumber = n 递归调用自身的次数(但现在使用 @ 987654327@ = 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 + S2 + ... + SN em> - S + SN - S (S - 1) + SN - S (S em> - 1) (S - 2) + ... + SN - S (S - 1) (S - 2) ... 1 =
= O(SN - SS !)。
但是每个findWordExists 调用(决赛除外)都会遍历每一边,所以我们需要将findWordExists 调用的数量乘以边的数量:S O em>(SN - SS!) = O (SN - S + 1S!) —这就是我们的时间复杂度。
更好的算法
实际上,您的问题是二分匹配问题,因此有比蛮力更有效的算法,例如Kuhn’s algorithm.
库恩算法的复杂度为O(N M),其中N为顶点数, M 是边数。在您的情况下,N 是立方体的数量,而 M 只是 N2,因此您的情况可能是 O(N3)。但是你还需要遍历所有立方体的所有边,所以如果立方体的边数大于N2,那么复杂度是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;
}
}