I am struggling to analyze the runtime complexity of the following algorithm formally:
Given a string s and a dictionary of words dict(wordDict), add spaces in s to construct a sentence where each word is a valid dictionary word.
Return all such possible sentences.
For example, given s = "catsanddog", dict = ["cat", "cats", "and", "sand", "dog"].
A solution is ["cats and dog", "cat sand dog"].
The proposed algorithm:
wordBreak(s, wordDict) {
res; //a container used to store the final result.
if(the length of s is zero or s is invalid) {
just output res directly.
}
if(we have already encountered s before) {
return the res that s corresponds to.
}
if(wordDict contains s) {
add s to res.
}
(looping index from 0 to the length of s) {
t = the substring of s starting from index to the end of s
if(wordDict contains t) {
temp = wordBreak(substring of s from 0 to index , wordDict);
if(temp is not empty) {
(looping index j from 0 to the length of temp) {
cancatenate the jth element of temp with t and add it to res.
}
}
}
}
store the result of s to storage.
return res;
}
}
My analysis:
Giving the algorithm, denote the length of $s$ by n. Suppose the runtime complexity is $T(n)$, then we would have something like $T(n) = T(n - 1) + T(n - 2) + \ldots + T(0) + n + 2^{n-1} - 1$, where I add $n$ because we are looping through $s$. In the meantime we are looping through temp which is a list of results. In the worst case temp can contain $2^{n-1} - 1$ results(subdivisions coming from a string of length $n - 1$). However this does not take into consideration the effect of memoization and I do not know how to achieve that. Specifically my question are:
What is the correct time complexity analysis? My guess is that it is at least $n 2^{n-1}$ which is the time taken to create the result list but this is a wild guess without formal proof.
How does the analysis differ from the case where there is no memoization.