算法导论读书笔记(18)

http://www.cnblogs.com/sungoshawk/p/3779740.html

算法导论读书笔记(18)

最长公共子序列

某给定序列的子序列,就是将给定序列中零个或多个元素去掉后得到的结果。其形式化定义如下:给定一个序列 X = < x1 , x2 , … , xm >,另一个序列 Z = < z1 , z2 , … , zk >,如果 Z 满足如下条件则称 Z 为 X 的 子序列 (subsequence),即存在一个严格递增的 X 的下标序列 < i1 , i2 , … , ik >,对所有 j = 1,2,…, k ,满足 xij = zj 。给定两个序列 X 和 Y ,如果 Z 既是 X 的子序列,也是 Y 的子序列,则称它是 X 和 Y 的 公共子序列 。

最长公共子序列问题 (longest-common-subsequence problem)就是给定两个序列 X = < x1 , x2 , … , xm >和 Y = < y1 , y2 , … , yn >,求 X 和 Y 长度最长的公共子序列。简称LCS问题。下面将展示如何用动态规划方法高效求解LCS问题。

步骤1:描述最长公共子序列的特征

LCS问题符合最优子结构的性质。可以看到,子问题的自然分类对应两个输入序列的“前缀”对。前缀的严格定义如下:给定一个序列 X = < x1 , x2 , … , xm>,对 i = 0,1,…, m ,定义 X 的第 i 前缀为 Xi = < x1 , x2 , … , xi >, X0 为空串。

定理 (LCS的最优子结构)

令 X = < x1 , x2 , … , xm >和 Y = < y1 , y2 , … , yn >为两个序列, Z = < z1 , z2 , … , zk >为 X 和 Y 的任意LCS。
1. 如果 xm = yn ,则 zk = xm = yn 且 Zk-1 是 Xm-1 和 Yn-1 的一个LCS。
2. 如果 xm ≠ yn ,那么 zk ≠ xm 意味着 Z 是 Xm-1 和 Y 的一个LCS。
3. 如果 xm ≠ yn ,那么 zk ≠ yn 意味着 Z 是 X 和 Yn-1 的一个LCS。

上面的定理说明两个序列的LCS包含两个序列的前缀的LCS。因此,LCS问题满足最优子结构性质。

步骤2:一个递归解

由定理可知,在求 X = < x1 , x2 , … , xm >和 Y = < y1 , y2 , … , yn >的一个LCS时,我们需要求解一个或两个子问题。如果 xm = yn ,我们应该求解 Xm-1和 Yn-1 的一个LCS。然后将 xm = yn 追加到这个LCS的末尾,就得到 X 和 Y 的一个LCS。如果 xm ≠ yn ,我们必须求解两个子问题:求 Xm-1 和 Y 的一个LCS与 X 和 Yn-1 的一个LCS。两个LCS中长的那个即为 X 和 Y 的一个LCS。

可以很容易看出LCS中的重叠子问题。为了求 X 和 Y 的一个LCS,我们可能需要求 X 和 Yn-1 的一个LCS以及 Xm-1 和 Y 的一个LCS。这几个子问题都包含求解 Xm-1 和 Yn-1 的LCS的子子问题。

设计LCS问题的递归算法还要建立最优解的递归式。令 c [ i , j ]表示 Xi 和 Yj 的LCS的长度。如果 i = 0或 j = 0,即一个序列长度为0,那么LCS的长度为0。根据LCS问题的最优子结构性质,可知:


步骤3:计算LCS的长度

过程 LCS-LENGTH 接受两个序列 X = < x1 , x2 , … , xm >和 Y = < y1 , y2 , … , yn >为输入。它将 c [ i , j ]的值保存在表 c [ 0 .. m , 0 .. n ],并按 行主次序(row-major order)计算表项(即首先由左至右计算 c 的第一行,然后第二行,依此类推)。过程还维护一个表 b [ 1 .. m , 1 .. n ]帮助构造最优解。 b [ i ,j ]指向的表项对应计算 c [ i , j ]时所选择的子问题的最优解。过程返回表 b 和表 c , c [ m , n ]保存了 X 和 Y 的LCS的长度。

LCS-LENGTH(X, Y)
1  m = X.length
2  n = Y.length
3  let b[1..m, 1..n] and c[0..m, 0..n] be new tables
4  for i = 1 to n
5      c[i, 0] = 0
6  for j = 0 to n
7      c[0, j] = 0
8  for i = 1 to m
9      for j = 1 to n
10         if x_i == y_j
11             c[i, j] = c[i - 1, j - 1] + 1
12             b[i, j] = "↖"
13         elseif c[i - 1, j] >= c[i, j - 1]
14             c[i, j] = c[i - 1, j]
15             b[i, j] = "↑"
16         else
17             c[i, j] = c[i, j - 1]
18             b[i, j] = "←"
19 return c and b

下图显示了 LCS-LENGTH 对输入序列 X = < A , B , C , B , D , A , B >和 Y = < B , D , C , A , B , A >生成的结果。过程的运行时间为 Θ ( mn ),因为每个表项的计算时间为 Θ ( 1 )。

步骤4:构造LCS

现在可以用 LCS-LENGTH 返回的表 b 快速构造 X = < x1 , x2 , … , xm >和 Y = < y1 , y2 , … , yn >的LCS。

PRINT-LCS(b, X, i, j)
1 if i == 0 or j == 0
2     return
3 if b[i, j] == "↖"
4     PRINT-LCS(b, X, i - 1, j - 1)
5     print x_i
6 elseif b[i, j] == "↑"
7     PRINT-LCS(b, X, i - 1, j)
8 else
9     PRINT-LCS(b, X, i, j - 1)

LCS问题的简单Java实现

参考自http://www.cs.cityu.edu.hk/~lwang/cs5302/LCS.java

private static int[][] lcsLength(String x, String y) {
    int m = x.length();
    int n = y.length();
    int[][] b = new int[m + 1][n + 1];
    int[][] c = new int[m + 1][n + 1];
    for (int i = 0; i < n; i++)
        c[i][0] = 0;
    for (int j = 0; j < m; j++)
        c[0][j] = 0;
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (x.charAt(i - 1) == y.charAt(j - 1)) {
                c[i][j] = c[i - 1][j - 1] + 1;
                b[i][j] = DIAGONAL;
            } else if (c[i - 1][j] >= c[i][j - 1]) {
                c[i][j] = c[i - 1][j];
                b[i][j] = UP;
            } else {
                c[i][j] = c[i][j - 1];
                b[i][j] = FORWARD;
            }
        }
    }
    return b;
}

public static String getLCS(String x, String y) {
    int[][] b = lcsLength(x, y);
    String lcs = "";
    int i = x.length();
    int j = y.length();
    while (i != 0 && j != 0) {
        if (b[i][j] == DIAGONAL) {
            lcs = x.charAt(i - 1) + lcs;
            i = i - 1;
            j = j - 1;
        }
        if (b[i][j] == UP) {
            i = i - 1;
        }
        if (b[i][j] == FORWARD) {
            j = j - 1;
        }
    }
    return lcs;
}

private static final int DIAGONAL = 1;
private static final int UP = 2;
private static final int FORWARD = 3;

基于Spring Boot搭建的一个多功能在线学习系统的实现细节。系统分为管理员和用户两个主要模块。管理员负责视频、文件和文章资料的管理以及系统运营维护;用户则可以进行视频播放、资料下载、参与学习论坛并享受个性化学习服务。文中重点探讨了文件下载的安全性和性能优化(如使用Resource对象避免内存溢出),积分排行榜的高效实现(采用Redis Sorted Set结构),敏感词过滤机制(利用DFA算法构建内存过滤树)以及视频播放的浏览器兼容性解决方案(通过FFmpeg调整MOOV原子位置)。此外,还提到了权限管理方面自定义动态加载器的应用,提高了系统的灵活性和易用性。 适合人群:对Spring Boot有一定了解,希望深入理解其实际应用的技术人员,尤其是从事在线教育平台开发的相关从业者。 使用场景及目标:适用于需要快速搭建稳定高效的在线学习平台的企业或团队。目标在于提供一套完整的解决方案,涵盖从资源管理到用户体验优化等多个方面,帮助开发者更好地理解和掌握Spring Boot框架的实际运用技巧。 其他说明:文中不仅提供了具体的代码示例和技术思路,还分享了许多实践经验教训,对于提高项目质量有着重要的指导意义。同时强调了安全性、性能优化等方面的重要性,确保系统能够应对大规模用户的并发访问需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值