深刻理解最长公共子串、最长公共子序列(应该比较全面)

本文详细探讨了最长公共子串和最长公共子序列的概念,通过图形化矩阵展示了动态规划的演变过程。作者提供C++代码实现,并进行了优化,包括二阶滚动数组和递归回溯的方法。此外,文章还讨论了当面对多个字符串或字符环时如何寻找最长公共子序列的挑战。
摘要由CSDN通过智能技术生成







最长公共子串篇(20191120)



理论知识:

推荐参考该博文:java实现字符串匹配问题之求两个字符串的最大公共子串
当然这篇也一样,看个人理解:求两个字符串的最长公共子串

图形理解:


矩阵初始化:

数据初始化

矩阵数值演变:

在这里插入图片描述

类似算法:

图论中的最短路径算法。
大致分有:迪杰斯特拉算法(Dijkstra)和弗洛伊德算法(Floyd)。
(对应着 贪心算法和动态规划 …… 别慌,名字起的高大尚并无影响理解。。。)
数据结构算法编程课、离散数学课、计算机网络课等都会涉及该算法。
本质都是化作矩阵,故线性代数一定要好好学。

最好的理解方式是什么:
亲自动手 —— 图解,自己手动在草稿纸上推演一遍(小矩阵即可)。

代码实现(C++):

太长时间没写C了,一入python差些找不着回头路(哈哈)
#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
typedef vector<string> VS;

// 为避免重复,检查当前子串是否为不存在,不存在时返回true
bool IsNoRepetition(string& , vector<string>& );

int main()
{
   
	string s1, s2;  // 待输入的两字符串
	string sameSubString;  // 临时存储相同子串
	VS sameSubStringVector;  // 保存所有相同子串
	while (cin >> s1 >> s2) {
   
		int max = 0;  // 最长子串长度(字符元素个数),初始置0、默认无相同子串
		int row = s1.length();  // 矩阵行数(长度)
		int col = s2.size();  // 矩阵列数(宽度)
		sameSubString.clear();  // 初始化时,需要重置中间存储变量(清空)
		// 申请动态二维数组
		// 也可以可直接 vector<vector<int>>不过建议多多学习、开拓视野
		int** dp = new int *[row];
		if (dp) {
   
			for (int i = 0; i < row; ++i) {
   
				dp[i] = new int[col];
			}
		}
		// 初始化矩阵,全部置false(即首先默认字符串不相同,其后相同再+1)
		// 此处是为了提醒学弟,注意学习 memset和fill的区别
		for (int i = 0; i < row; ++i) {
   
			/*for (int j = 0; j < col; ++j) {
				dp[i][j] = 0;
			}*/
			fill(dp[i], dp[i] + col, 0);  // 两种初始化方式
		}

		for (int i = 0; i < row; ++i) {
   
			for (int j = 0; j < col; ++j) {
   
				int iTemp = i, jTemp = j;  // 临时变量
				while (s1[iTemp] == s2[jTemp]) {
   
					dp[iTemp][jTemp] = dp[iTemp][jTemp] + 1;
					sameSubString += s1[iTemp];
					iTemp++;
					jTemp++;
					// 横纵都 +1是为了斜对角线(即 s1和s2串都往后移动一位)
					// 值得注意的是别造成数组越界(程序健壮性问题、bug)
					if (iTemp == row || jTemp == col) {
   
						break;
					}
				}
				//  相同子串不为空(即存在时)
				if (!sameSubString.empty()) {
   
					//cout << "sameSubString = " << sameSubString << endl;  // 通过输出测试结果,是否如预期所想
					if (IsNoRepetition(sameSubString, sameSubStringVector)) {
   
						sameSubStringVector.push_back(sameSubString);
					}
					sameSubString.clear();  // 每遍历过一次相同子串,最后记得重置为空(细节)
				}
			}
		}
		// 矩阵变换完成后,查找最大值(即为最长相同子串长度)
		for (int i = 0; i < row; i++) {
   
			for (int j = 0; j < col; j++) {
   
				if (max < dp[i][j]) {
   
					max = dp[i][j];
				}
			}
		}
		if (sameSubStringVector.empty()) {
   
			cout << endl;
		}
		else {
   
			// 将各个相同子串按照字典顺序排序
			sort(sameSubStringVector.begin(), sameSubStringVector.end());
			for (VS::iterator iter = sameSubStringVector.begin(); iter != sameSubStringVector.end(); ++iter) {
   
				// 直接输出的所有的相同子串
				//cout << *iter << endl;
				// 使用条件判断只输出最长的相同子串
				if ((*iter).size() == max) {
   
					cout << *iter << endl;
					break;  // break是为了只输出一个最长的公共子串,即ASCLL码最小的那个
				}
			}
		}
		//cout << endl;  // 此空行是为了排版好看,避免pe格式出错
		// 每执行一遍程序,重置为初始状态(为空)。至于,不放在else内,是编码经验释然。
		sameSubStringVector.clear();
		// new了内存空间就要delete
		// 注意这种表达方式
		for (int i = 0; i < row; ++i) {
   
			delete[] dp[i];
		}
		delete[] dp;
	}
	return 0;
}


bool IsNoRepetition(string& str, vector<string>& vs) {
   
	for (int i = 0; i < vs.size(); ++i) {
   
		if (vs[i] == str)
			return false;	//有重
	}
	return true;	//无重
}

代码设计满足的要求:
对于每组测试数据,输出最大子串。
如果最大子串为空(即不存在),则输出一个空行。
测试样例:
输入:
abcded123456aabbcc
abcdaa1234
输出:
1234

代码理解:
本人代码
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最长公共子序列(Longest Common Subsequence, LCS)和最长公共子串(Longest Common Substring)是两个常见的字符串相关问题最长公共子序列是指给定两个字符串,要求找到它们之间最长的公共子序列长度子序列是从原字符串删除若干个字符而得到的新字符串,字符在新字符串的相对顺序与原字符串的保持一致。动态规划是求解LCS问题的常用方法。 以字符串s1 = "ABCBDAB"和s2 = "BDCAB"为例,可以使用动态规划的方法求解最长公共子序列长度。首先创建一个二维数组dp,dp[i][j]表示s1的前i个字符和s2的前j个字符之间的最长公共子序列长度,那么有以下推导关系: 1. 当i=0或j=0时,dp[i][j]=0。 2. 当s1[i-1]=s2[j-1]时,dp[i][j] = dp[i-1][j-1] + 1。 3. 当s1[i-1]!=s2[j-1]时,dp[i][j] = max(dp[i-1][j], dp[i][j-1])。 最后,dp[len(s1)][len(s2)]即为最长公共子序列长度。 对于最长公共子串,要求找到两个字符串最长的公共连续子串的长度。连续子串是指在原字符串连续出现的字符子序列。同样可以使用动态规划来解决该问题。 仍以上述两个字符串s1和s2为例,创建一个二维数组dp,dp[i][j]表示以s1[i-1]和s2[j-1]为结尾的公共子串的长度,那么有以下推导关系: 1. 当i=0或j=0时,dp[i][j]=0。 2. 当s1[i-1]=s2[j-1]时,dp[i][j] = dp[i-1][j-1] + 1。 3. 当s1[i-1]!=s2[j-1]时,dp[i][j] = 0。 最后,dp矩阵的最大值即为最长公共子串长度。 以上就是求解最长公共子序列最长公共子串的常见方法。在实际应用,我们可以根据具体的问题选择合适的方法,并结合动态规划来解决这些字符串相关的问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值