循环内递归函数的时间复杂度分析

时间:2013-08-25 17:06:07

标签: algorithm time-complexity

我试图分析以下功能的时间复杂度。此函数用于检查字符串是否由其他字符串组成。

set<string> s; // s has been initialized and stores all the strings
bool fun(string word) {
    int len = word.size();

    // something else that can also return true or false with O(1) complexity

    for (int i=1; i<=len; ++i) {
       string prefix = word.substr(0,i);
       string suffix = word.substr(i);
       if (prefix in s && fun(suffix))
           return true;
       else
           return false;
    }
}

我认为时间复杂度为O(n),其中n是单词的长度(我是对的吗?)。但由于递归是在循环内部,我不知道如何证明它。

编辑:

此代码不是正确的C++代码(例如prefix in s)。我只是展示了这个功能的想法,并想知道如何分析它的时间复杂度

2 个答案:

答案 0 :(得分:4)

分析这种方法的方法是根据输入的长度和前缀在s中的(未知)概率开发递归关系。假设前缀在s中的概率由前缀长度L的某个函数pr(L)给出。让复杂性(操作次数)由T(len)给出。

如果len == 0(word是空字符串),则T = 1.(该函数在循环后缺少最后的return语句,但我们假设实际代码只是概念的草图,而不是实际执行的内容。

对于每个循环迭代,用T(len; i)表示循环体复杂度。如果前缀不在s中,那么正文具有恒定的复杂度(T(len; i)= 1)。此事件的概率为1 - pr(i)。

如果前缀在s中,则该函数根据对true的递归调用返回falsefun(suffix),其具有复杂度T(len - i) 。该事件具有概率pr(i)。

因此,对于i的每个值,循环体复杂度为:

  

T(len; i)= 1 *(1 - pr(i))+ T(len - i)* pr(i)

最后(这取决于预期的逻辑,而不是发布的代码),我们有

  

T(len)= sum i = 1 ... len (T(len; i))

为简单起见,我们将pr(i)视为值为0.5的常数函数。那么T(len)的递归关系是(直到一个常数因子,这对O()计算来说并不重要):

  

T(len)= sum i = 1 ... len (1 + T(len - i))= len + sum i = 0 ... len-1 < /子>(T(i))的

如上所述,边界条件是T(0)= 1.这可以通过标准递归函数方法来解决。让我们看看前几个术语:

len   T(len)
0     1
1     1 + 1 = 2
2     2 + 2 + 1 = 5
3     3 + (4 + 2 + 1) = 11
4     4 + (11 + 5 + 2 + 1) = 23
5     5 + (23 + 11 + 5 + 2 + 1) = 47

模式显然是T(len)= 2 * T(len - 1)+ 1.这对应于指数复杂性:

  

T(n)= O(2 n

当然,这个结果取决于我们对pr(i)的假设。 (例如,如果对于所有i,pr(i)= 0,则T(n)= O(1)。如果pr(i)具有最大前缀长度-p(i)=,则也将存在非指数增长0为所有i> M为某些M.)pr(i)独立于i的假设可能是不现实的,但这实际上取决于s的填充方式。

答案 1 :(得分:0)

假设你已经修复了其他人注意到的错误,那么i值就是字符串被拆分的位置(每个i是最左边的拆分点,然后你就可以解决所有错误i)的右侧。这意味着如果要展开递归,您将查看最多n-1个不同的分割点,并询问每个子字符串是否为有效字。如果word的开头没有来自你的集合中的很多元素,那么事情就可以了,因为那时你可以跳过递归。但在最坏的情况下,prefix in s始终为真,您尝试n-1分裂点的每个可能子集。这给出了2^{n-1}个不同的分裂集,乘以每个这样的集合的长度。