下面的代码计算s1是否可以减少到t,如果是这样,有多少种方式。 假设s1的长度为n,t的长度为m。下面代码的最坏情况运行时是O(n ^ m)而没有memoization。假设我们可以记住s1的子问题,即子串重复。运行时间为O(m * n)。因为我们需要为每个n重复m次。这个推理是否正确?
static int distinctSeq(String s1, String t) {
if (s1.length() == t.length()) {
if (s1.equals(t))
return 1;
else
return 0;
}
int count = 0;
for (int i = 0; i < s1.length(); i++) {
String ss = s1.substring(0, i)+ s1.substring(i + 1);
count += distinctSeqRec(ss, t);
}
return count;
}
答案 0 :(得分:0)
正如@meowgoesthedog已经提到的,您的初始解决方案的时间复杂度为O(n!/m!)
:
s1
长度为n
,n > m
,那么您可以通过从原始字符串中排除一个符号来进入n
个不同的状态。m
。使用给定算法从m
获得n
长度的方式为n*(n-1)*(n-2)*...*(m+1)
,这实际上是n!/m!
m
的初始字符串中排除符号而形成的每个长度为n
的字符串,您必须比较来自s1
和t
的字符串,这将需要m
次操作(字符串的长度),因此上一步的复杂度应乘以m
,但考虑到你在big-O中有阶乘,另一个*m
获胜&# 39;改变渐近复杂度。现在关于memoization。如果添加memoization,则算法将仅转换为尚未访问的状态,这意味着该任务将计算s1
的唯一子串的数量。为简单起见,我们将考虑s1
的所有符号都不同。长度为x
的状态数是从n-x
中删除不同s1
符号的方式的数量,无视订单。实际上是binomial coefficient - C(n,x) = n!/((n-x)! * x!)
。
算法将在n
和m
之间转换所有长度,因此整体时间复杂度为Sum(k=m...n, C(n,k)) = n!/((n-1)!*1!) + n!/((n-2)!*2! + ... + n!/((n-k)!*k!)
。考虑到我们正在计算渐近复杂度,我们感兴趣的是该和的最大成员,即k
尽可能接近n/2
的成员。如果m
小于n/2
,则总和中会出现C(n, n/2)
,否则C(n,m)
是其中最大的元素,因此带有记忆的算法的复杂性为{ {1}}。