具有O(m * n)的最长公共子串非DP解决方案

时间:2016-04-24 23:30:25

标签: string algorithm dynamic-programming

问题的定义是:

  

给定两个字符串,找到最长的公共子字符串。

     

返回它的长度。

我正在解决这个问题,我想我用O(m * n)时间复杂度解决了这个问题。但是我不知道为什么当我查找解决方案时,所有人都在谈论动态编程的最佳解决方案 - http://www.geeksforgeeks.org/longest-common-substring/

以下是我的解决方案,您可以在此处进行测试:http://www.lintcode.com/en/problem/longest-common-substring/

int longestCommonSubstring(string &A, string &B) {

    int ans = 0;
    for (int i=0; i<A.length(); i++) {
        int counter = 0;
        int k = i;
        for (int j=0; j<B.length() && k <A.length(); j++) {

            if (A[k]!=B[j]) {
                counter = 0;
                k = i;

            } else {
                k++;
                counter++;
                ans = max(ans, counter);

            }  
        }
    }

    return ans;        
}

我的想法很简单,从字符串A的第一个位置开始,看看我能与字符串B匹配的最长子字符串是什么,然后从字符串A的第二个位置开始,看看我能匹配的最长子字符串是什么...

我的解决方案有问题吗?或者它不是O(m * n)复杂度?

2 个答案:

答案 0 :(得分:2)

好消息:你的算法是O(mn)。坏消息:它无法正常工作。

你的内循环是错误的:它打算在B中找到A [i:]中最长的初始子串,但它的工作原理如下:

j = 0
While j < len(B)
   Match as much of A[i:] against B[j:]. Call it s.
   Remember s if it's the longest so far found.
   j += len(s)

找不到最长的匹配。例如,当A =“XXY”且B =“XXXY”且i = 0时,它将找到“XX”作为最长匹配而不是完整匹配“XXY”。

这是您的代码的可运行版本(轻微转录为C),显示错误结果:

#include <string.h>
#include <stdio.h>

int lcs(const char* A, const char* B) {
    int al = strlen(A);
    int bl = strlen(B);
    int ans = 0;
    for (int i=0; i<al; i++) {
        int counter = 0;
        int k = i;
        for (int j=0; j<bl && k<al; j++) {
            if (A[k]!=B[j]) {
                counter = 0;
                k = i;
            } else {
                k++;
                counter++;
                if (counter >= ans) ans = counter;
            }  
        }
    }
    return ans;        
}

int main(int argc, char**argv) {
    printf("%d\n", lcs("XXY", "XXXY"));
    return 0;
}

运行此程序会输出“2”。

答案 1 :(得分:0)

您的解决方案是O(nm)复杂度,如果您将结构与提供的算法进行比较,则完全相同;但是,你的不会记忆。

链接中提供的动态算法的一个优点是,在相同的复杂度类时间内,它可以在O(1)中调用不同的子串长度;否则,它对我来说很好。

这是一种不时发生的事情,因为存储子空间解决方案并不总是会产生更好的运行时间(在第一次调用时)并导致相同的复杂性类运行时(例如,尝试计算第n个)斐波纳契数字与动态解决方案并将其与尾递归解决方案进行比较。请注意,在这种情况下,就像你的情况一样,在第一次填充数组之后,每次连续调用都会更快地返回一个答案。