最长公共子串-朴素算法不用dp怎么达到O(n^2)

教科书里都是这么讲最长公共子串
方法1. 比较两个串里的每个可能子串,O(n^4)
方法2. 只比较方法1中的其中一个串的前缀形式,O(n^3)
到此为止还是最朴素的方法,然后就:
方法3. 动态规划,O(n^2)

看到这里,我就很奇怪,为什么要用动态规划,难道不能改进方法2吗,同时比较两个串的前缀形式吗,这样就可以O(n^2)了。

下面尝试给出一个算法:
对字符串A的每个字母为起始的前缀子串,单遍扫描字符串B

b_i = B[0]
for each a_i in A:
    for  a_j,b_j start from current a_i,b_i:
        if  a_j  equals b_j:
              update answer
              next j
        else:
             b_i := b_j    // skip scanned string in B
             next a_i
             break
        end if
    end for
end for

当然仅仅这样是错的,很容易注意到有个问题,比如
A: ababc
B: abababc
最长子串应该是ababc。选定A中首字母a为起始,单遍扫描B,B中的前4个字母与A的前4个字母,已经匹配掉了,不会再返回去。B只剩下最后3个字母,无法再发现最大子串。

但有个解决办法,把A和B换下
A:abababc
B:ababc
就可以了。

所以,给出的算法就是交换AB用上述算法做2次,取大的,时间复杂度还是O(N)
有人可能不信,觉得会有漏洞。
于是我就给出了一个证明:

1476764154873.jpg
1476764236857.jpg

代码如下

int LCS(char astring[], char bstring[]) {
    int max_size = 0;

    for (int i = 0; i<strlen(astring); i++) {
        int current_max_size = 0;
        int j = 0, tmp_i = i;
        while (j < strlen(bstring) && tmp_i < strlen(astring)) {
            if (astring[tmp_i] == bstring[j]) {
                current_max_size += 1;
                j++;
                tmp_i++;
            } else {
                if (current_max_size == 0) {
                    ++j;
                }else {
                    max_size = current_max_size > max_size ? current_max_size : max_size;
                    current_max_size = 0;
                    tmp_i = i;
                }
            }
        }
        max_size = current_max_size>max_size ? current_max_size : max_size;
    }
    return max_size;
}

最后记得正反扫描两次:

int n1 = LCS(astring, bstring);
int n2 = LCS(bstring, astring);
int n = n1 > n2 ? n1 : n2;

这个题是SPOJ1811,但是O(N^2)的算法毫无疑问地超时了。

说明还有改进的空间,据说有个后缀树的算法,是O(N)复杂度的,下次学习下。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,789评论 0 33
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,131评论 19 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,805评论 18 399
  • Rxjava中涉及到两个重要的角色,一:被观察者 二:观察者,我们知道当被观察者和观察者绑定后,被观察者就可以发...
    加油码农阅读 576评论 0 0
  • 这个功能是为了查找连接路由器的有哪些设备,从而进行网络的管理,对设备进行网络禁用,添加黑名单等等。其实还可以根据设...
    徐胖胖no1阅读 1,403评论 2 0