大白话讲解LCS(最长公共子序列)

本文通过实例解析LCS(最长公共子序列)的概念,指出它与最长公共子串的区别,并提供了求解LCS的方法,包括特殊情况的处理,帮助读者深入理解这一算法。

今天看了七月在线算法课。再一次认识了LCS,现在整理记录:

LCS(Longest Common Subsequence)最长公共子序列。

一个序列S任意删除若干个字符得到新序列T,那么T叫做S的子序列。

两个序列X和Y的公共子序列中,长度最长的那个叫X和Y的最长公共子序列。

例如:

字符串13455和245576的最长公共子序列为455.

字符串acdfg和adfc的最长公共子序列为adf.

注意:这里要区别最长公共子串LCS(Longest Common Substring),这个要求的是字符串必须连续。

这里偷懒,直接拿了老师的PPT。记好下面的记号。

下面就进行LCS解法的探索:

当x=y的时候:

然后我们就能得到:

此处的意思就是:当两个子序列中结尾字符是相等的,那么最长的子序列就肯定是这个相等值+前面的的最长长度了,这样他们的长度才能是最长的。

例子:

那么现在我们再考虑X不等于Y的情况:

此处的解释:

当我们认为是我们子序列的子串的最后一个元素不相等,那么他的最长子序列一定来自两个地方,假设去掉X序列的最后一个元素,那么最长子序列一定产生于图中A+B+Yn,同理一样, B+A+Xn.

例子:

总结:

怎么求呢?C[m,n]是一个正整数的值,表示序列m和n之间的最大的长度。

当两个字符串有一个是0 ,那么LCS一定是0;

例子:

右下角的数据怎么计算呢?

经过上面的分析,我们知道两个序列有一个是0 的那么LCS都是0 ;所以第0行第0列都是0.

图中红圈内1怎么得来的呢?

红圈内的x是B,y是D,即x不等于y.那么我们就要看红圈上左边和上边 的数值了,选择一个大的数值写入。

那么以此类推,当x=y 的时候,我们直接选择红圈左上的值+1,写入即可。

两个例子来自点击打开链接

代码一是让你输入两个序列,然后输出最长公共子序列和长度。

#include <stdio.h>  
#include <string.h>  
#include <stdlib.h>  
int LCSLength(char* str1, char* str2, int **b)  
{  
    int i,j,length1,length2,len;  
    length1 = strlen(str1);  
    length2 = strlen(str2);  
  
    //双指针的方法申请动态二维数组  
    int **c = new int*[length1+1]; //共有length1+1行  
    for(i = 0; i < length1+1; i++)  
        c[i] = new int[length2+1];//共有length2+1列  
  
    for(i = 0; i < length1+1; i++)  
        c[i][0]=0;        //第0列都初始化为0  
    for(j = 0; j < length2+1; j++)  
        c[0][j]=0;        //第0行都初始化为0  
  
    for(i = 1; i < length1+1; i++)  
    {  
        for(j = 1; j < length2+1; j++)  
        {  
            if(str1[i-1]==str2[j-1])//由于c[][]的0行0列没有使用,c[][]的第i行元素对应str1的第i-1个元素  
            {  
                c[i][j]=c[i-1][j-1]+1;  
                b[i][j]=0;          //输出公共子串时的搜索方向  
            }  
            else if(c[i-1][j]>c[i][j-1])  
            {  
                c[i][j]=c[i-1][j];  
                b[i][j]=1;  
            }  
            else  
            {  
                c[i][j]=c[i][j-1];  
                b[i][j]=-1;  
            }  
        }  
    }  
    /* 
    for(i= 0; i < length1+1; i++) 
    { 
    for(j = 0; j < length2+1; j++) 
    printf("%d ",c[i][j]); 
    printf("\n"); 
    } 
    */  
    len=c[length1][length2];  
    for(i = 0; i < length1+1; i++)    //释放动态申请的二维数组  
        delete[] c[i];  
    delete[] c;  
    return len;  
}  
void PrintLCS(int **b, char *str1, int i, int j)  
{  
    if(i==0 || j==0)  
        return ;  
    if(b[i][j]==0)  
    {  
        PrintLCS(b, str1, i-1, j-1);//从后面开始递归,所以要先递归到子串的前面,然后从前往后开始输出子串  
        printf("%c",str1[i-1]);//c[][]的第i行元素对应str1的第i-1个元素  
    }  
    else if(b[i][j]==1)  
        PrintLCS(b, str1, i-1, j);  
    else  
        PrintLCS(b, str1, i, j-1);  
}  
  
int main(void)  
{  
    char str1[100],str2[100];  
    int i,length1,length2,len;  
    printf("请输入第一个字符串:");  
    gets(str1);  
    printf("请输入第二个字符串:");  
    gets(str2);  
    length1 = strlen(str1);  
    length2 = strlen(str2);  
    //双指针的方法申请动态二维数组  
    int **b = new int*[length1+1];  
    for(i= 0; i < length1+1; i++)  
        b[i] = new int[length2+1];  
    len=LCSLength(str1,str2,b);  
    printf("最长公共子序列的长度为:%d\n",len);  
    printf("最长公共子序列为:");  
    PrintLCS(b,str1,length1,length2);  
    printf("\n");  
    for(i = 0; i < length1+1; i++)//释放动态申请的二维数组  
        delete[] b[i];  
    delete[] b;  
    system("pause");  
    return 0;  
}  

<think>嗯,用户让我用大白话解释Git是什么。之前我已经给过比较专业的解释,但用户这次特别强调要通俗易懂,还提供了几个引用资源,都是强调"小白视角"和"大白话"的。 用户可能是个编程新手,或者之前被专业术语吓到过。ta提到的引用[1]里说"看过就忘""苦不堪言",引用[2][3]都反复强调"大白话",说明用户需要完全避开技术黑话。 我注意到上次回答虽然全面,但用了"分布式版本控制""SHA-1哈希"这类术语。这次得彻底转化:把Git比喻成游戏存档系统可能不错,因为存档/读档是常见体验。引用[2]提到"git和github是两码事"这点很重要,很多新手会混淆。 核心要点应该是: - 版本控制=游戏存档(随时保存不怕玩砸) - 分支=平行宇宙(实验性操作不毁原世界) - 远程仓库=云存档(多设备同步+队友共享) 避免提到任何命令,重点说清概念。用户引用[3]提到"跳过理论直接讲命令行"的问题,所以这次连git add/commit这些基础命令都先不提,纯讲原理。 最后用"写小说改稿子"的生活化场景收尾,这样哪怕完全不懂编程的人也能理解。相关问题的设计也要延续这个风格,比如"手滑删错文件怎么找回"这种实际痛点。</think>好的,咱们用最直白的大白话来聊聊 Git 是什么,就像聊天一样: **想象一下你在写一篇重要的小说(或者做一份重要的报告、设计一个图纸):** 1. **你会不断修改:** 今天写一章,明天觉得不好删掉重写,后天又想加个人物。 2. **你会担心出错:** 万一改了半天,发现还是之前的好,想找回来怎么办?万一不小心删掉了重要段落怎么办? 3. **你可能需要和别人合作:** 比如你和朋友一起写,你写一部分,他写一部分,最后怎么合到一起? **Git 就是帮你解决这些头疼问题的超级智能“时光机”+“协作神器”!** **它具体能干啥?** 1. **📸 自动“拍照存档”(版本控制):** * 每当你觉得写到一个不错的地方(比如完成一小节、修复了一个bug),你就可以告诉 Git:“嘿,给我现在这个状态拍个快照存起来!” * 这样,**无论你后面怎么改、怎么删、甚至改得一塌糊涂,你随时都能“回到”之前拍过的任何一个快照的状态**。就像游戏存档一样,玩砸了还能读档重来!再也不用怕改错了东西找不回来。[^1][^3] 2. **🌳 开“平行宇宙”做实验(分支管理):** * 你想尝试一个大胆的新剧情(或者一个新功能),但又怕把现在好好的稿子搞乱? * Git 可以让你**瞬间复制一份当前的小说副本(创建一个“分支”)**,在这个安全的“平行宇宙”里随便折腾。改好了,满意了,再把“平行宇宙”里的成果合并回主稿子;改砸了?直接把这个分支删掉,主稿子完全不受影响![^1][^3] 3. **🤝 和别人一起写不打架(团队协作):** * 你和朋友都要写同一篇小说。Git 提供了一个**公共的“云盘”(远程仓库,比如 GitHub、GitLab 等)**。 * 你先把你写好的部分存到本地 Git 仓库(拍照存档),然后**“上传”(push)** 到云盘上。 * 你的朋友就能**“下载”(pull)** 你写的最新内容到他自己的电脑上。 * 他改完他那部分,也拍照存档,再上传到云盘。 * 这样你们就能互相看到对方的进度和修改,Git 还会尽量帮你们**自动合并**各自的修改。万一你们改到了同一句话(冲突),Git 也会标出来让你们商量着解决。[^2][^4] 4. **🔒 保证稿子安全(数据完整性):** * Git 给每次“拍照存档”都生成一个**独一无二的“指纹”**。一旦存档,里面的内容就**不可能被悄悄篡改**。如果有人改了,指纹就对不上了,马上就能发现。[^3] **总结一下,Git 是啥?** * **一个超级智能的“时光机”**:随时回到你作品的任何一个历史版本。 * **一个安全的“实验沙盒”**:让你在“平行宇宙”里大胆尝试新想法,不影响主线。 * **一个高效的“团队协作工具”**:让大家安全地一起修改同一份作品,互相能看到进度,自动合并修改。 * **一个可靠的“历史记录本”**:完整记录作品的每一次变化,保证记录真实可靠。 **简单说:Git 就是帮你管理文件(尤其是代码)所有修改历史,并且让你能安全、方便地和别人一起干活的神奇工具!** 它本身是个软件(工具),而 GitHub 这类网站是提供存放你 Git 仓库的“云盘”服务(仓库)。[^2][^3] --- **
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值