序言
这次遇到贝壳花名的需求,需要使用最长公共子串对花名做校验。这种算法在面试题中算是必会题,各种四层循环,三层循环,两层循环的代码在我脑中闪过,但是今天就是要带你实现不一样的最长公共子串!
教科书式实现
使用动态规划,两层循环,使用二维数组存储状态,时间复杂度O(n^2^),空间复杂度O(n^2^)或O(2n)
一张图解释原理:
1 | 先 横 向 处 理 |
优化空间复杂度至O(1)
从上图可以发现,在纵向累加时实际只需要左上方的计数器即可, O(n^2^)的空间白白被浪费了,最优的空间复杂度应该是O(1)。那么该如何处理呢?
一张图解释原理:
e a b c b c f
+---+ +---+---+---+---+---+---+---+
a | 0 | | 1 | 0 | 0 | 0 | 0 | 0 | a
+-------+ +-----------------------+
b | 0 | 0 | | 2 | 0 | 1 | 0 | 0 | b
+-----------+ --------------------+
c | 0 | 0 | 0 | | 3 | 0 | 2 | 0 | c
+---------------+ ----------------+
d | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | d
+-------------------+ +-----------+
e | 1 | 0 | 0 | 0 | 0 | | 0 | 0 | e
+---+---+---+---+---+-------+ +---+---+
e a b c b c f
答案就是沿着等长对着线处理。
有意思的代码抽象
大家可以根据上面思路写一下,一般会把算法分成两部分:处理长方形的左下部分和处理长方形的右上部分,两部分都是双层循环,时间复杂度和空间负载度已经变为了O(n^2^) ,O(1)。
肯定有人已经发现自己的代码处理左下角的代码和处理右上角的代码不能复用,一个是从中间向左下角处理,一个是从中间向右上角处理,明明很类似,但是就是没发合并。
那么有没有方法把这两部分处理抽象成公共代码呢?不卖关子了,直接上图:
1 | + |
如果你想使用公共代码同时实现处理左下角和右上角是不可能的了。所以你需要把右上角的三角翻折,然后你就得到了两个三角:
1 | + |
这样就变成了处理两遍左下角了,代码也可以完美复用!!!
最终实现
我的完整思考过程已经分析完毕,这样沿对着线处理还有一个小小的优点:提前结束搜索。这一点大家可以自行思考,这里不做过多解释。
直接干货上场:
1 | public class Solution { |
结尾
怎么样,经历这次优化过程是否感觉自己对最长公共子串的认识又更深了一步呢?虽然不能保证是首创(也可能是首创?),但是这次一步一步真切思考优化直到获得成果让我无比兴奋。
说了这么多,我就是要给我们贝壳招聘开发组打个广告>_>,期待更多爱思考优秀的同学加入!
![](/Users/sage/Desktop/屏幕快照 2018-11-10 13.33.20.png)