C 什么';这个算法的运行时间是多少?

C 什么';这个算法的运行时间是多少?,c,algorithm,complexity-theory,C,Algorithm,Complexity Theory,我刚刚写了一个问题的答案: 这个函数应该在两个字符串之间找到最长的子字符串,但是当我试图找出最坏情况下的运行时以及导致这种情况的输入时,我意识到我不知道。考虑代码是C伪代码。 // assume the shorter string is passed in as A int lcs(char * A, char * B) { int length_a = strlen(A); int length_b = strlen(B); // This holds the length

我刚刚写了一个问题的答案:

这个函数应该在两个字符串之间找到最长的子字符串,但是当我试图找出最坏情况下的运行时以及导致这种情况的输入时,我意识到我不知道。考虑代码是C伪代码。
// assume the shorter string is passed in as A
int lcs(char * A, char * B)
{
  int length_a = strlen(A);
  int length_b = strlen(B);

  // This holds the length of the longest common substring found so far
  int longest_length_found = 0;

  // for each character in one string (doesn't matter which), look for 
  //   incrementally larger strings in the other
  // once a longer substring can no longer be found, stop
  for (int a_index = 0; a_index < length_a - longest_length_found; a_index++) {
    for (int b_index = 0; b_index < length_b - longest_length_found; b_index++) {

      // check the next letter until a mismatch is found or one of the strings ends.
      for (int offset = 0; 
           A[a_index+offset] != '\0' && 
             B[b_index+offset] != '\0' && 
             A[a_index+offset] == B[b_index+offset]; 
           offset++) {          
        longest_length_found = longest_length_found > offset ? longest_length_found : offset;
      }
    }
  }
  return longest_found_length;
}
我是接近了还是错过了什么或误用了什么

我很确定我可以使用trie/前缀树做得更好,但我还是很想了解这段特定代码的行为。

我认为roliu在评论中说的话非常划算。我认为你的算法是O(N3),最好的情况是O(N2)

我实际上想指出的是这个算法的过度放纵。您可以看到,对于每个字符串中的每个可能的起始偏移量,您将测试每个后续匹配字符以计算匹配数。但是考虑这样的事情:

A = "01111111"
B = "11111110"
几乎你会发现的第一件事是从
A[1]
B[0]
开始的最大匹配子字符串,然后你会测试部分精确的重叠,从
A[2]
开始,
B[1]
等等。。。这里重要的是相对偏移量。通过实现这一点,您可以完全删除算法的N3部分。然后就变成了将一个数组移到另一个数组之下的问题

A         01111111
B  11111110
B   11111110
B    11111110
B        ... -->
B                11111110
为了降低代码的复杂性,您可以只测试系统的一半,然后交换阵列并测试另一半:

// Shift B under A
A  01111111
B  11111110
B      ... -->
B         11111110

// Shift A under B
B  11111110
A  01111111
A      ... -->
A         01111111
如果你这样做,你会得到类似于O((A+B-2)*min(A,B)/2),或者更方便的O(N2)


因此,在我们在评论中讨论了它之后,我们一致认为问题在于为代码找到最坏的运行时。通过以下证明,我们可以声称它至少是ω(n^3):


A=aaaa…aabb…bbbb
意思是
A=n
,它由
n/2
A
n/2
b

B=aaaa….
其中
B=n

现在我们考虑最外循环的第一个<代码> N/2</Cult>迭代(即第一个<代码> N/2 <代码> A<代码>字符串的起始索引)。修复最外层循环的第一个

n/2
迭代中的一些迭代
i
。第二个循环的上限至少为
n-n/2=n/2
,因为两个字符串的LCS的长度为
n/2
。对于第二个循环的每次迭代,我们匹配一个长度为
n/2-i
的字符串(您可以用矛盾来证明这一点)。因此,在最外层循环的第一次
n/2
迭代之后,该行:

longest\u length\u found=longest\u length\u found>偏移量?找到的最长长度:偏移量

已运行:

n/2*(n/2)+n/2*(n/2-1)+n/2*(n/2-2)+……+n/2*(2)+n/2*(1)=n/2*ω(n^2)=ω(n^3)

具体来说,对于最外层循环的第一次迭代,我们在字符串
a
中有一个
n/2
a
,在
B
中有
n/2
起始点。对于
B
中的每个起始点,我们将匹配长度为
n/2
的完整公共子字符串(这意味着我们将命中该行
n/2
次)。这就是
n/2*(n/2)
。对于最外层循环的下一次迭代,我们在字符串
a
中有一个
n/2-1
a
,在
B
中仍然有
n/2
起始点。在这种情况下,我们为每个起始索引
=>n/2(n/2-1)
匹配一个长度为
n/2-1
的公共子字符串。同一个参数在
i=n/2
中起归纳作用


无论如何,我们知道算法在输入端的运行时间比最外层循环的第一次
n/2
迭代的运行时间要长,所以它也是
Omega(n^3)

通常,当我们谈论运行时复杂性时,我们谈论一些情况,例如“平均情况”、“最坏情况”或“预期运行时”(在随机算法的情况下)。这里,您似乎已经将可能的输入划分为任意集合,并说“这在
O(n^3)
中运行,而这在
O(n^2)
中运行。”.我只是建议,也许有必要再研究一下定义,因为这个问题的定义有点模糊。我还想说,如果字符串相等,它就不是线性的……但这取决于你的成本模型。我认为答案是,除非我能优化到字符串长度的平方根,运行时并没有变得更好——优化只会减少最坏的情况。是的,我试图了解最坏的情况是什么,最坏的情况是什么数据集。我更新了问题,使其更具体。我非常确定,如果字符串相等,它是线性的。当a_索引和b_索引都为0时,第一次er most for循环的偏移量从0到字符串的长度,每次迭代都会做恒定的功。在这一点上,找到的最长的_length_将设置为字符串的长度,导致第二个for循环终止,然后外部for循环也从1开始终止!<0(字符串长度-本身)我没有注意到你从循环的上限中减去了最长的长度。我认为这不正确……如果
B
中最长的公共子序列(意思是
B
中的副本)会怎么样以
B
中的最后一个字符结尾?基本上,
A=asbb
B=axbb
对于相同的字符串,我的算法有一个明确的O(N)的最佳情况。“几乎你会发现的第一件事是从A[1]和B[0]开始的最大匹配子字符串,然后你将测试精确重叠的部分,从A开始[2] ,B[1]“--那不是真的。它将在找到一个subs后立即停止
// Shift B under A
A  01111111
B  11111110
B      ... -->
B         11111110

// Shift A under B
B  11111110
A  01111111
A      ... -->
A         01111111
int lcs_half(char * A, char * B)
{
    int maxlen = 0, len = 0;
    int offset, i;
    for( offset = 0; B[offset]; offset++ )
    {
        len = 0;
        for( i = 0; A[i] && B[i+offset]; i++ )
        {
            if( A[i] == B[i+offset] ) {
                len++;
                if( len > maxlen ) maxlen = len;
            }
            else len = 0;
        }
    }
    return maxlen;
}

int lcs(char * A, char * B)
{
    int run1 = lcs_half(A,B);
    int run2 = lcs_half(B,A);
    return run1 > run2 ? run1 : run2;
}