If exists if (cubeMatrix.length != word.length()) return false; , and each side letter of the cube is unique (i.e. no two sides of the cube have the same letter), then the time complexity of your algorithm is O (S N - S + 1 S!), When N> = S and O ( SN!), When N <= S. Here S is the number of sides of the cube, and N is the number of cubes.
In short, you make a recursive call only when there is an unused letter in the word corresponding to the letter on the side of the cube, so in the worst case, the number of times you make a recursive call is no more than the number of words with letters left unused. And the number of unused letters of the word decreases with increasing depth of recursion, and ultimately this number becomes less than the number of sides of the cube. Therefore, in the final recursive depths, complexity becomes a factorial.
A bit more
We introduce f (n) how many times you call findWordExists with cubeNumber = n. We also introduce g (n) how many times findWordExists with cubeNumber = n calls itself recursively (but now with cubeNumber = n + 1).
f (0) = 1 because you are calling findWordExists not recursively only once.
f (n) = f (n - 1) g (n - 1) for n> 0.
We know that g (n) = min {S, N - n}, because, as I have already pointed out, findWordExists called recursively no more than the number of letters left - if (frequency > 0) is responsible for this; and the number of remaining letters is equal to the number of remaining cubes, i.e. N - n.
Now we can calculate how many times findWordExists is called in total:
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 (S - 1) + S <sup = N - S (S - 1) (S - 2) + ... + S N - S (S - 1) (S - 2) ... 1 =
= O (S N - S S!).
But each call to findWordExists (except for the finals) iterates on each side, so we need to multiply the number of calls to findWordExists by the number of sides: SO (S N - S S!) = O (S N - S + 1 S!) - and this is our temporary complexity.
Best algorithm
Actually, your problem is the problem of two-way matching, so there are much more efficient algorithms than brute force, for example. Kuhns algorithm .
The complexity of the Kuhn algorithm is O (NM), where N is the number of vertices and M is the number of edges. In your case, N is the number of cubes, and M is just N 2, so the complexity in your case can be O (N 3 ). But you also need to iterate over all sides of all cubes, so if the number of sides of the cube is greater than N 2 then the complexity is O (NS), where S is the number of sides of the cube.
Here is a possible implementation:
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; } }