最长公共子序列(LCS)到git diff
当宿命从你门前走过的时候,芸芸众生总是显得那么渺小
0x00 LCS
最长公共子序列
跟最长不降子序列
以及最长公共字串
都不是一回事。这种用搜索时间复杂度太高的问题一般都会选择用DP来解决。状态转移方程为:
$$c[i,j] = \begin{cases}
0, & i=0 \text{ or } j=0
\\ c[i-1,j-1]+1, & x_i =y_j
\\ max(c[i-1,j],c[i,j-1]), &x_i \neq y_i
\end{cases}$$
然后我们顺手就可以写出伪代码:
1 | memset(c,0) |
那么问题来了,开个二维数组进行dp实在是太浪费空间了。仔细观察上面的伪代码,i作为最外层循环在循环内部只调用了i和i-1两个数值,所以事实上只需要当前行(i)
和前一行(i-1)
。这篇文章有一个很优美的解决方案,定义一个2×len(x)
的数组,和pre
now
两个变量作为指针,每一次循环分别交换这两个变量的值。伪码如下:
1 | memset(c,0) |
通过交换指针的值来实现交换两行数组确实实现了函数的优美。那么还有没有更优的方案了呢。
经过分析,对于pre行,其实也只是使用了第j和j-1列。类系这篇文章我们定义两个变量leftabove, above
代替pre行
。
1 | memset(c,0) |
这样,空间便缩减为了len(x)+2
0x01 diff
现在广泛采用的 diff 算法,也是类似上面所说的 LCS 算法。但为什么对两个超大的文件进行diff依然速度飞快呢?现在的diff基本上采用的是 Eugene Myers 的论文 An O(ND) Difference Algorithm and its Variations 上面写的方法的变种
现在实在看不懂,留个坑回头慢慢写
为了生存你展翅高飞
但是你要学会随波逐流
才能在汹涌的浪尖上从容不迫
否则就是铤而走险,自掘坟墓