引言
在前文中,已经利用分治思想,将原本需要O(mn)空间复杂度的动态规划问题转换成只需要O(m+n),进一步地,在本文中,我们考虑能否将时间复杂度也降低。最终实现的结果是将O(mn)的时间复杂度降低到O(αmax{m,n})。
减少冗余计算
问题分析
考虑如果两个需要对齐的字符串很不相似(例如“gold”,“time”),我们是否还有必要用动态规划的方式对其计算相似度?设计动态规划对齐两个字符串的目的(我们从应用出发),如修正用户的错误输入,对比文章是否抄袭,这些都建立在两者较为相似的情况下(这个较为相似的标准由使用者制定)。因此,我们可以对那些很多字符对不上的字符串做一个预处理,直接在这个阶段停止,减少没必要的计算,而相似度较高的两个字符串体现在动态规划中有什么特点?
箭头的方向:
- 向上的箭头:S用空格与T中字符对齐
- 向左的箭头:T用空格与S中字符对齐
- 斜向上的箭头:S与T中字符与字符对齐(可能相等&不等)
可以发现像左下角-30这样的犄角旮旯,根本不会影响到最终路径的结果,预处理去掉很不相似的单词,可以保证最终的路径应该在对角线附近,所以我们真正需要计算的是对角线附近的区域。
解决思路
通过设置一个参数α在原数组上开出一个条偏离带,如下图:
在原动态数组中即如下:
伪代码对比
原伪代码采用双重for循环,将每一个格子的结果计算出来,进行了太多不必要冗余计算,如下:
设置了α之后伪代码如下:
将不足条带α长度的部分补全,可以发现总的计算格子数量不大αmax{m,n}新的时间复杂度是O(αmax{m,n})。
总结
通常我们降低算法运行时间复杂度办法是减少冗余计算,我们通过观察回溯路径,发现我们真正要考虑的是那些相似度较高的情况,对于一些差异较大的,我们直接在预处理部分就可以去掉。因此我们发现这类结果路径通常位于对角线附近,这就发现侧边角的结果实际上是冗余计算。为了避免,设计了步骤如下:
- 将可能区域用条带包裹起来,这个条带的表现就是参数α
- 对数组对重新定义计算方式(BANDED−DP(T,S,α)),多考虑了一层其是否在条带内的因素,这里减少了冗余计算
从序列联对的案例可以看出,为了减少实际使用过程的时间复杂度,我们可以通过结合应用将所有可能案例进行拆分,对于明显不成立的情况,可以在预处理阶段进行筛除,为后续降低时间复杂度提供基础。