基于C#实现最长公共子序列

news2024/9/22 17:17:13

一、作用

最长公共子序列的问题常用于解决字符串的相似度,是一个非常实用的算法,作为码农,此算法是我们的必备基本功。

二、概念

举个例子,cnblogs 这个字符串中子序列有多少个呢?很显然有 27 个,比如其中的 cb,cgs 等等都是其子序列,我们可以看出子序列不见得一定是连续的,连续的那是子串。
我想大家已经了解了子序列的概念,那现在可以延伸到两个字符串了,那么大家能够看出:cnblogs 和 belong 的公共子序列吗?
在你找出的公共子序列中,你能找出最长的公共子序列吗?
image.png

三、解决方案

<1> 枚举法

这种方法是最简单,也是最容易想到的,当然时间复杂度也是龟速的,我们可以分析一下,刚才也说过了cnblogs的子序列个数有27个 ,延伸一下:一个长度为N的字符串,其子序列有2N个,每个子序列要在第二个长度为N的字符串中去匹配,匹配一次需要O(N)的时间,总共也就是O(N*2N),可以看出,时间复杂度为指数级,恐怖的令人窒息。

<2> 动态规划

既然是经典的题目肯定是有优化空间的,并且解题方式是有固定流程的,这里我们采用的是矩阵实现,也就是二维数组。
第一步:先计算最长公共子序列的长度。
第二步:根据长度,然后通过回溯求出最长公共子序列。
现有两个序列 X={x1,x2,x3,…xi},Y={y1,y2,y3,…,yi},设一个 C[i,j]: 保存 Xi 与 Yj 的 LCS 的长度。
递推方程为:
image.png
不知道大家看懂了没?动态规划的一个重要性质特点就是解决“子问题重叠”的场景,可以有效的避免重复计算,根据上面的公式其实可以发现 C[i,j]一直保存着当前(Xi,Yi)的最大子序列长度。

 using System;
 namespace ConsoleApplication2
 {
     public class Program
     {
         static int[,] martix;
 
         static string str1 = "cnblogs";
         static string str2 = "belong";
 
         static void Main(string[] args)
         {
             martix = new int[str1.Length + 1, str2.Length + 1];
 
             LCS(str1, str2);
 
             //只要拿出矩阵最后一个位置的数字即可
             Console.WriteLine("当前最大公共子序列的长度为:{0}", martix[str1.Length, str2.Length]);
 
             Console.Read();
         }
 
         static void LCS(string str1, string str2)
         {
             //初始化边界,过滤掉0的情况
             for (int i = 0; i <= str1.Length; i++)
                 martix[i, 0] = 0;
 
             for (int j = 0; j <= str2.Length; j++)
                 martix[0, j] = 0;
 
             //填充矩阵
             for (int i = 1; i <= str1.Length; i++)
             {
                 for (int j = 1; j <= str2.Length; j++)
                 {
                     //相等的情况
                     if (str1[i - 1] == str2[j - 1])
                     {
                         martix[i, j] = martix[i - 1, j - 1] + 1;
                     }
                     else
                     {
                         //比较“左边”和“上边“,根据其max来填充
                         if (martix[i - 1, j] >= martix[i, j - 1])
                             martix[i, j] = martix[i - 1, j];
                         else
                             martix[i, j] = martix[i, j - 1];
                     }
                 }
             }
         }
     }
 }

image.png
图大家可以自己画一画,代码完全是根据上面的公式照搬过来的,长度的问题我们已经解决了,这次要解决输出最长子序列的问题,我们采用一个标记函数 Flag[i,j],当
①:C[i,j]=C[i-1,j-1]+1 时 标记 Flag[i,j]=“left_up”; (左上方箭头)
②:C[i-1,j]>=C[i,j-1] 时 标记 Flag[i,j]=“left”; (左箭头)
③: C[i-1,j]<C[i,j-1] 时 标记 Flag[i,j]=“up”; (上箭头)
例如:我输入两个序列 X=acgbfhk,Y=cegefkh。

 using System;
 
 namespace ConsoleApplication2
 {
     public class Program
     {
         static int[,] martix;
 
         static string[,] flag;
 
         static string str1 = "acgbfhk";
 
         static string str2 = "cegefkh";
 
         static void Main(string[] args)
         {
             martix = new int[str1.Length + 1, str2.Length + 1];
 
             flag = new string[str1.Length + 1, str2.Length + 1];
 
             LCS(str1, str2);
 
             //打印子序列
             SubSequence(str1.Length, str2.Length);
 
             Console.Read();
         }
 
         static void LCS(string str1, string str2)
         {
             //初始化边界,过滤掉0的情况
             for (int i = 0; i <= str1.Length; i++)
                 martix[i, 0] = 0;
 
             for (int j = 0; j <= str2.Length; j++)
                 martix[0, j] = 0;
 
             //填充矩阵
             for (int i = 1; i <= str1.Length; i++)
             {
                 for (int j = 1; j <= str2.Length; j++)
                 {
                     //相等的情况
                     if (str1[i - 1] == str2[j - 1])
                     {
                         martix[i, j] = martix[i - 1, j - 1] + 1;
                         flag[i, j] = "left_up";
                     }
                     else
                     {
                         //比较“左边”和“上边“,根据其max来填充
                         if (martix[i - 1, j] >= martix[i, j - 1])
                         {
                             martix[i, j] = martix[i - 1, j];
                             flag[i, j] = "left";
                         }
                         else
                         {
                             martix[i, j] = martix[i, j - 1];
                             flag[i, j] = "up";
                         }
                     }
                 }
             }
         }
 
         static void SubSequence(int i, int j)
         {
             if (i == 0 || j == 0)
                 return;
 
             if (flag[i, j] == "left_up")
             {
                 Console.WriteLine("{0}: 当前坐标:({1},{2})", str2[j - 1], i - 1, j - 1);
 
                 //左前方
                 SubSequence(i - 1, j - 1);
             }
             else
             {
                 if (flag[i, j] == "up")
                 {
                     SubSequence(i, j - 1);
                 }
                 else
                 {
                     SubSequence(i - 1, j);
                 }
             }
         }
     }
 }

image.png
image.png
好,我们再输入两个字符串:

static string str1 = "abcbdab";
static string str2 = "bdcaba";

image.png
image.png
通过上面的两张图,我们来分析下它的时间复杂度和空间复杂度。
**时间复杂度:**构建矩阵我们花费了 O(MN)的时间,回溯时我们花费了 O(M+N)的时间,两者相加最终我们花费了 O(MN)的时间。
**空间复杂度:**构建矩阵我们花费了 O(MN)的空间,标记函数也花费了 O(MN)的空间,两者相加最终我们花费了 O(MN)的空间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1229062.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IDEA运行 支付宝案例

我 | 在这里 &#x1f575;️ 读书 | 长沙 ⭐软件工程 ⭐ 本科 &#x1f3e0; 工作 | 广州 ⭐ Java 全栈开发&#xff08;软件工程师&#xff09; &#x1f383; 爱好 | 研究技术、旅游、阅读、运动、喜欢流行歌曲 &#x1f3f7;️ 标签 | 男 自律狂人 目标明确 责任心强 ✈️公…

庖丁解牛:NIO核心概念与机制详解 01 _ 入门篇

文章目录 Pre输入/输出Why NIO流与块的比较通道和缓冲区概述什么是缓冲区&#xff1f;缓冲区类型什么是通道&#xff1f;通道类型 NIO 中的读和写概述Demo : 从文件中读取1. 从FileInputStream中获取Channel2. 创建ByteBuffer缓冲区3. 将数据从Channle读取到Buffer中 Demo : 写…

优思学院|什么是精益生产管理?从一个生活上的故事出发来说明。

你关掉电脑&#xff0c;离开办公室。 一个小时后&#xff0c;你进入家门和孩子们在一起。 你和家人一起吃晚饭。 你的老板打电话来查看你的项目进展。 你哄孩子入睡并给他们读个故事。 作为一个负责任的父母&#xff0c;你想要与孩子们的互动时间增加并提高生活的质量&…

flink 查看写入starrocks的数据量 总行数

针对该connector: https://github.com/StarRocks/docs.zh-cn/blob/main/loading/Flink-connector-starrocks.md

使用 uWSGI 部署 Django 应用详解

概要 部署 Django 应用到生产环境是一个至关重要的步骤&#xff0c;其中选择合适的 WSGI 服务器对于确保应用的稳定性和性能至关重要。uWSGI 是一个流行的选择&#xff0c;它不仅高效、轻量&#xff0c;还非常灵活。本文将详细介绍如何使用 uWSGI 来部署 Django 应用&#xff…

力扣C++学习笔记——C++ assign全面解析

cassign是一个C20标准中新增的头文件&#xff0c;主要提供了assign函数&#xff0c;用于将一个容器内的元素按照特定规则赋值到另一个容器中。它是STL容器操作的重要一环&#xff0c;具有高效、简洁、易用的特点。 assign函数有多个版本&#xff0c;一般使用的是容器类型相同或…

掌握Python中classmethod的妙用,提升代码灵活性与可维护性

概要 在Python编程中&#xff0c;classmethod是一种非常有用的装饰器&#xff0c;它可以将一个方法转换为类方法&#xff0c;使得该方法可以通过类名或实例名直接调用&#xff0c;而不需要传入self参数。通过合理使用classmethod&#xff0c;我们可以提高代码的灵活性、复用性…

基于C#实现字符串相似度

一、概念 对于两个字符串 A 和 B&#xff0c;通过基本的增删改将字符串 A 改成 B&#xff0c;或者将 B 改成 A&#xff0c;在改变的过程中我们使用的最少步骤称之为“编辑距离”。比如如下的字符串&#xff1a;我们通过种种操作&#xff0c;痉挛之后编辑距离为 3&#xff0c;不…

8.6 矢量图层点要素基于规则(Rule-based)渲染使用

文章目录 前言基于规则&#xff08;Rule-based&#xff09;QGis代码实现 总结 前言 前面介绍了矢量-点要素-单一符号、矢量-点要素-分类符号以及矢量-点要素-分级符号的使用本章介绍如何使用基于规则的渲染说明&#xff1a;文章中的示例代码均来自开源项目qgis_cpp_api_apps …

00后如何组织双十一大促看这一篇就够了! | 京东云技术团队

引言 大家好&#xff0c;我是王蒙恩&#xff0c;一名“整顿职场”的00后。作为一名去年刚刚加入京东的校招生&#xff0c;我有幸成为本次CDP平台的11.11备战负责人。虽然早在实习的时候就经历过大促&#xff0c;但是真正组织整个部门的备战还是很难忘的。于是提起笔&#xff0…

二阶段提交

二阶段提交 二阶段提交&#xff08;英语&#xff1a;Two-phase Commit&#xff09;是指&#xff0c;为了使基于分布式系统架构下的所有节点在进行事务提交时保持一致性而设计的一种算法(Algorithm)。 二阶段过程 在两阶段提交过程中&#xff0c;主要分为了两种角色协调者&…

实时监控电脑屏幕的软件丨同时查看12台电脑屏幕

Hello 大家好 又见面啦 今天给大家推荐两款比较实用的监控电脑使用情况、屏幕的软件&#xff01; 软件一 实时性能监控 从软件名就可以看出来&#xff0c;这是一款电脑性能监测工具。它可以实时监测内存、CPU、磁盘占用情况&#xff0c;也能一键结束进程&#xff0c;给电脑提…

MES管理系统与ERP系统的实施顺序与决策

在现今的数字化时代&#xff0c;制造企业纷纷寻求通过先进的系统来提升运营效率。其中&#xff0c;ERP管理系统与MES管理系统被誉为是数字化转型的两大利器。然而&#xff0c;在推进这两个系统时&#xff0c;企业常常面临一个关键问题&#xff1a;究竟应该先实施哪一个系统&…

ASUS华硕ROG幻13笔记本电脑GV301QE原厂Windows10系统

链接&#xff1a;https://pan.baidu.com/s/1aPW0ctRXRNAhE75mzVPdTg?pwdds78 提取码&#xff1a;ds78 华硕玩家国度幻13笔记本电脑锐龙版Ryzen 7 5800HS,显卡3050 3050Ti,3060,3060Ti,3070,3070Ti 原厂W10系统自带所有驱动、出厂主题壁纸、系统属性专属LOGO标志、Office办…

Python武器库开发-flask篇之error404(二十七)

flask篇之error404(二十七) 首先&#xff0c;我们先进入模板的界面创建一个404的html页面 cd templates vim 404.html404.html的内容如下&#xff1a; <h1>error!!!</h1>在 Flask 应用程序中&#xff0c;当用户访问一个不存在的页面的时候&#xff0c;会出现 4…

(一)RISC-V 指令集及寄存器介绍

1. RISC-V指令集介绍 RISC-V 念作 “risk-five”&#xff0c;代表着 Berkeley 所研发的第五代精简指令集。 该项目 2010 年始于加州大学伯克利&#xff08;Berkeley&#xff09;分校&#xff0c;希望选择一款 ISA用于科研和教学。经过前期多年的研究和选型&#xff0c;最终决定…

DE算法简介

文章目录 前言一、DE是什么&#xff1f;二、DE流程2.1 初始化种群2.2 变异&#xff08;差分操作&#xff09;2.3 交叉2.4 选择2.5 重复迭代 三、DE运行结果 前言 这两天看了DE算法&#xff0c;简单说下自己的认识 一、DE是什么&#xff1f; 百科定义&#xff1a;差分进化算…

Linux+qt:创建动态库so,以及如何使用(详细步骤)

目录 1、根据安装Qt Creator的向导进行创建 2、开发动态库注意的一些细节 3、给动态库添加一个对外开放的接口文件 4、了解下Qt的 .pri文件&#xff08;非常实用&#xff09; 5、如何调用动态库.so 1、根据安装Qt Creator的向导进行创建 &#xff08;1&#xff09;选择“…

BetterDisplay Pro v2.0.11(显示器颜色校准软件)

BetterDisplay Pro是一款为Mac电脑设计的屏幕亮度调节软件&#xff0c;旨在提高显示器的色彩和亮度表现。它可以根据用户的需求和显示器的特性&#xff0c;自动调整显示器的亮度、色温、对比度等参数&#xff0c;以获得更加真实、舒适的视觉效果。 这款软件拥有智能调节功能&a…

pyhton重启Deployment和状态

import os import timefrom kubernetes import client, config# 指定配置文件路径 config.load_kube_config(config_fileconfig)# 创建 Kubernetes API 客户端 v1 client.AppsV1Api() v2 client.CoreV1Api() # 指定命名空间 namespace default# 指定 Deployments 名称列表 d…