子序列、子串问题

本文介绍了一种求解最长公共子序列(LCS)问题的算法实现,包括打印任意一个公共子序列及所有公共子序列的方法。通过动态规划确定两个字符串间的最长公共子序列,并提供代码实现。
#include<iostream>
#include <vector>
using namespace std;

//enum decreaseDire {kInit = 0,kLeft = 1,kUp = 2,kLeftUp = 4};
enum decreaseDire {kInit = 0,kLeft = 1,kUp = 2,kLeftUp = 4};

//打印任意一个公共子序列
void LCS_Print(int **LCS_direction,const char* pStr1,const char* pStr2,size_t row,size_t col)
{
	if(!pStr1 || !pStr2)
		return ;
	size_t length1 = strlen(pStr1);
	size_t length2 = strlen(pStr2);
	if(length1 == 0 || length2 == 0)
		return ;
	if(row>=length1 || col>=length2 || row<0 || col<0)
		return ;
	if(LCS_direction[row][col] == kLeftUp){
		LCS_Print(LCS_direction,pStr1,pStr2,row-1,col-1);	printf("%c",pStr1[row]);
	}else if(LCS_direction[row][col] == kLeft){
		LCS_Print(LCS_direction,pStr1,pStr2,row,col-1);
	}else if(LCS_direction[row][col] == kUp){
		LCS_Print(LCS_direction,pStr1,pStr2,row-1,col);
	}
	return ;
}
//打印任意所有公共子序列
void LCS_Print_AllSeq(int **LCS_direction,const char* pStr1,const char* pStr2,size_t row,size_t col,char *result,int start)
{
	if(!pStr1 || !pStr2)
		return ;
	size_t length1 = strlen(pStr1);
	size_t length2 = strlen(pStr2);
	if(length1 == 0 || length2 == 0)
		return ;
	if(row>=length1 || col>=length2 || row<0 || col<0){
		printf("%s\n",result+start+1);
		return ;
	}
	switch(LCS_direction[row][col]){
		case kLeft:
			LCS_Print_AllSeq(LCS_direction,pStr1,pStr2,row,col-1,result,start);
			break;
		case kUp:
			LCS_Print_AllSeq(LCS_direction,pStr1,pStr2,row-1,col,result,start);
			break;
		case kLeftUp:
			result[start] = pStr1[row];
			LCS_Print_AllSeq(LCS_direction,pStr1,pStr2,row-1,col-1,result,start-1);	//printf("%c",pStr1[row]);
			break;
		case kLeft+kUp:
			LCS_Print_AllSeq(LCS_direction,pStr1,pStr2,row,col-1,result,start);
			LCS_Print_AllSeq(LCS_direction,pStr1,pStr2,row-1,col,result,start);
			break;
		case kLeft+kLeftUp:
			LCS_Print_AllSeq(LCS_direction,pStr1,pStr2,row,col-1,result,start);
			LCS_Print_AllSeq(LCS_direction,pStr1,pStr2,row-1,col-1,result,start-1);	//printf("%c",pStr1[row]);
			break;
		case kUp+kLeftUp:
			LCS_Print_AllSeq(LCS_direction,pStr1,pStr2,row-1,col,result,start);
			LCS_Print_AllSeq(LCS_direction,pStr1,pStr2,row-1,col-1,result,start-1);	//printf("%c",pStr1[row]);
			break;
		case kLeft+kUp+kLeftUp:
			LCS_Print_AllSeq(LCS_direction,pStr1,pStr2,row,col-1,result,start);
			LCS_Print_AllSeq(LCS_direction,pStr1,pStr2,row-1,col,result,start);
			LCS_Print_AllSeq(LCS_direction,pStr1,pStr2,row-1,col-1,result,start-1);	//printf("%c",pStr1[row]);
			break;
	}
// 	if(LCS_direction[row][col] == kLeftUp){
// 		LCS_Print(LCS_direction,pStr1,pStr2,row-1,col-1);	printf("%c",pStr1[row]);
// 	}
// 	if(LCS_direction[row][col] == kLeft){
// 		LCS_Print(LCS_direction,pStr1,pStr2,row,col-1);
// 	}
// 	if(LCS_direction[row][col] == kUp){
// 		LCS_Print(LCS_direction,pStr1,pStr2,row-1,col);
// 	}
}
//subsequence
int LCS(const char* pStr1,const char* pStr2)
{
	if(!pStr1 || !pStr2)
		return 0;
	size_t length1 = strlen(pStr1);
	size_t length2 = strlen(pStr2);
	if(length1 == 0 || length2 == 0)
		return 0;

	int **LCS_length,**LCS_direction;
	size_t i,j;
	LCS_length = new int*[length1];	LCS_direction = new int*[length1];
	for(i=0;i<length1;i++){
		LCS_length[i] = new int[length2];	LCS_direction[i] = new int[length2];
	}
	for(i=0;i<length1;i++){
		for(j=0;j<length2;j++){
			LCS_length[i][j] = 0;	LCS_direction[i][j] = kInit;
		}
	}
	for(i=0;i<length1;i++){
		for(j=0;j<length2;j++){
			if(i==0 || j==0){
				if(pStr1[i] == pStr2[j]){
					LCS_length[i][j] = 1;					LCS_direction[i][j] = kLeftUp;
				}
			}else if(pStr1[i] == pStr2[j]){
				LCS_length[i][j] = LCS_length[i-1][j-1]+1;	LCS_direction[i][j] = kLeftUp;
			}else if(LCS_length[i-1][j] >= LCS_length[i][j-1]){
				LCS_length[i][j] = LCS_length[i-1][j];		LCS_direction[i][j] = kUp;
			}else{
				LCS_length[i][j] = LCS_length[i][j-1];		LCS_direction[i][j] = kLeft;
			}
			//printf("%2d ",LCS_length[i][j]);
			printf("%2d ",LCS_direction[i][j]);
		}
		printf("\n");
	}
	LCS_Print(LCS_direction,pStr1,pStr2,length1-1,length2-1);
	return LCS_length[length1-1][length2-1];
}
//AllSeq
int LCS_AllSeq(const char* pStr1,const char* pStr2)
{
	if(!pStr1 || !pStr2)
		return 0;
	size_t length1 = strlen(pStr1);
	size_t length2 = strlen(pStr2);
	if(length1 == 0 || length2 == 0)
		return 0;

	int **LCS_length,**LCS_direction;
	size_t i,j;
	LCS_length = new int*[length1];	LCS_direction = new int*[length1];
	for(i=0;i<length1;i++){
		LCS_length[i] = new int[length2];	LCS_direction[i] = new int[length2];
	}
	for(i=0;i<length1;i++){
		for(j=0;j<length2;j++){
			LCS_length[i][j] = 0;	LCS_direction[i][j] = kInit;
		}
	}
	for(i=0;i<length1;i++){
		for(j=0;j<length2;j++){
			if(i==0 && j==0){
				if(pStr1[i] == pStr2[j]){
					LCS_length[i][j] = 1;					LCS_direction[i][j] += kLeftUp;
				}
			}else if(i==0 || j==0){
				if(pStr1[i] == pStr2[j]){
					LCS_length[i][j] = 1;					LCS_direction[i][j] += kLeftUp;
				}
				if(i==0){
					if(LCS_length[i][j-1] == 1){
						LCS_length[i][j] = 1;
						LCS_direction[i][j] += kLeft;
					}
				}else{
					if(LCS_length[i-1][j] == 1){
						LCS_length[i][j] = 1;
						LCS_direction[i][j] += kUp;
					}
				}
			}
			if(i!=0 && j!=0){
				if(pStr1[i] == pStr2[j]){
					LCS_length[i][j] = LCS_length[i-1][j-1]+1;	LCS_direction[i][j] += kLeftUp;
					if(LCS_length[i-1][j] == LCS_length[i][j]){
						LCS_direction[i][j] += kUp;
					}
					if(LCS_length[i][j-1] == LCS_length[i][j]){
						LCS_direction[i][j] += kLeft;
					}
				}else{
					if(LCS_length[i-1][j] == LCS_length[i][j-1]){
						LCS_length[i][j] = LCS_length[i-1][j];
						LCS_direction[i][j] += kUp;
						LCS_direction[i][j] += kLeft;						
					}else if(LCS_length[i-1][j] > LCS_length[i][j-1]){
						LCS_length[i][j] = LCS_length[i-1][j];
						LCS_direction[i][j] += kUp;
					}else{
						LCS_length[i][j] = LCS_length[i][j-1];
						LCS_direction[i][j] += kLeft;			
					}
				}
			}
			printf("%2d ",LCS_direction[i][j]);
			//printf("%2d ",LCS_length[i][j]);
		}
		printf("\n");
	}
	char *result = new char[length1+1];
	result[length1] = '\0';
	LCS_Print_AllSeq(LCS_direction,pStr1,pStr2,length1-1,length2-1,result,length1-1);
	return LCS_length[length1-1][length2-1];
}
//substring
int LCString(const char* pStr1,const char* pStr2)
{
	if(!pStr1 || !pStr2)
		return 0;
	size_t length1 = strlen(pStr1);
	size_t length2 = strlen(pStr2);
	if(length1 == 0 || length2 == 0)
		return 0;

	int **LCS_length,**LCS_direction;
	size_t i,j;
	LCS_length = new int*[length1];	LCS_direction = new int*[length1];
	for(i=0;i<length1;i++){
		LCS_length[i] = new int[length2];	LCS_direction[i] = new int[length2];
	}
	for(i=0;i<length1;i++){
		for(j=0;j<length2;j++){
			LCS_length[i][j] = 0;	LCS_direction[i][j] = kInit;
		}
	}
	int maxLen = 0;int maxI = 0,maxJ = 0;
	for(i=0;i<length1;i++){
		for(j=0;j<length2;j++){
			if(i==0 || j==0){
				if(pStr1[i] == pStr2[j]){
					LCS_length[i][j] = 1;					LCS_direction[i][j] = kLeftUp;
				}
			}else if(pStr1[i] == pStr2[j]){
				if(LCS_direction[i-1][j-1] = kLeftUp){
					LCS_length[i][j] = LCS_length[i-1][j-1]+1;	LCS_direction[i][j] = kLeftUp;
				}
				else{
					LCS_length[i][j] = 1;						LCS_direction[i][j] = kLeftUp;
				}
			}
			if(LCS_length[i][j] > maxLen){
				maxLen = LCS_length[i][j];
				maxI = i;	maxJ = j;
			}
		}
	}
	//LCS_Print(LCS_direction,pStr1,pStr2,length1-1,length2-1);
	//return LCS_length[length1-1][length2-1];
	LCS_Print(LCS_direction,pStr1,pStr2,maxI,maxJ);
	return maxLen;
}

int main()
{
	char Str1[] = "BDCABA",Str2[] = "ABCBDAB";
	//char Str1[] = "dhajhfakdfjkahfahakjhfjahyfhgqioqurjdnachhzjjckaghda",Str2[] = "qyuiaudhajndjahdfjajdalkfdyi8qyahjkdhajhdfahfjhasfka";
	//char Str1[] = "dhajhfakdfjkahfahakjhfa",Str2[] = "qyuiaudhajndjahdhasfka";
	int Len;
	//Len = LCS(Str1,Str2);			//子串
	//Len = LCS_AllSeq(Str1,Str2);	//所有子串
	Len = LCString(Str1,Str2);		//子序列
	cout<<endl<<Len<<endl;
	return 0;
}
<think>首先,用户的问题是:“我想理解子序列子串在计算机科学中的区别 请问子序列子串的区别 计算机科学 基础概念”。这涉及到计算机科学中的基本概念。 根据系统指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 - 回答结构清晰,帮助用户逐步解决问题。 参考站内引用: - 引用[1]提到:子序列子串是基本概念,在公共子序列问题中常用。 - 引用[2]:字符串的最长公共子序列(LCS)是动态规划的范畴。 - 引用[3]:子序列是从序列中派生出来的序列,元素相对顺序不变;子序列可以不连续。 - 引用[4]:LCS问题中,子序列可以不是连续的,但相对顺序一样;子串是连续的。 - 引用[5]:子序列可以不连续,而子串要求连续。 关键区别: - 子串(Substring):必须是原字符串中连续的一段字符序列。 - 子序列(Subsequence):可以从原序列中删除一些元素(不一定连续),但保持元素的相对顺序。 例如: - 对于字符串 "ABCD": - 子串: "AB", "BC", "CD", "ABC" 等(连续)。 - 子序列: "AC", "AD", "BD" 等(不连续,但顺序保持)。 在数学表达式中: - 如果提到序列,可以用数学符号表示。例如,序列 $S = s_1, s_2, \dots, s_n$。 - 子序列: $S' = s_{i_1}, s_{i_2}, \dots, s_{i_k}$ where $1 \leq i_1 < i_2 < \dots < i_k \leq n$。 - 子串: $S' = s_i, s_{i+1}, \dots, s_j$ for some $1 \leq i \leq j \leq n$。 回答结构: 1. **引入概念**:解释子序列子串的定义。 2. **区别对比**:清晰地列出主要区别。 3. **例子说明**:用简单例子展示区别。 4. **应用场景**:简要提及在计算机科学中的应用,如最长公共子序列(LCS)问题。 5. **生成相关问题**:在末尾添加相关问题。 生成相关问题: - 基于主题,如: 1. 最长公共子序列问题如何解决? 2. 子序列子串在算法设计中的重要性? 3. 实际应用场景,如生物信息学或文本比较。 引用标识:在回答中,如果引用参考内容,添加引用标识如 [^1]。参考引用中,[1]到[5]都涉及子序列子串,所以可以在相关部分添加。 完整回答: - 用中文。 - 结构清晰:先定义,再区别,例子,应用。 - 确保LaTeX语法正确。</think>### 子序列子串在计算机科学中的区别 在计算机科学中,子序列子串序列处理中的基础概念,常用于字符串比较、算法设计(如动态规划)和生物信息学等领域。它们虽然都表示从原序列中派生出的部分序列,但核心区别在于**连续性要求**和**元素顺序**。下面我将逐步解释它们的定义、区别、示例和应用,帮助您清晰理解。 #### 1. **基本定义** - **子串(Substring)**:指原字符串中**连续的一段字符序列**。子串必须保持元素的连续性和相对顺序,不能跳过任何元素。 例如,对于字符串 $S = "ABCD"$,子串可以是 $"AB"$ 或 $"BCD"$(因为它们是连续的片段)。 - **子序列(Subsequence)**:指从原序列中**删除部分元素后得到的序列**,但保留元素的**相对顺序**。子序列不要求元素连续,可以跳过中间元素。 例如,对于字符串 $S = "ABCD"$,子序列可以是 $"AC"$ 或 $"BD"$(因为 $"A"$ 和 $"C"$ 之间跳过了 $"B"$,但顺序不变)。 #### 2. **关键区别对比** 下表总结了子序列子串的核心差异: | 特性 | 子串(Substring) | 子序列(Subsequence) | |--------------|----------------------------------------|---------------------------------------| | **连续性** | 必须连续(元素相邻) | 可以不连续(可以跳过元素) | | **元素顺序** | 必须保持原顺序 | 必须保持原顺序 | | **数学表示** | 对于序列 $S = s_1, s_2, \dots, s_n$,子串为 $S_{i:j} = s_i, s_{i+1}, \dots, s_j$($1 \leq i \leq j \leq n$) | 子序列为 $S' = s_{k_1}, s_{k_2}, \dots, s_{k_m}$($1 \leq k_1 < k_2 < \dots < k_m \leq n$) | | **示例** | $"ABC"$ 是 $"ABCD"$ 的子串 | $"AC"$ 是 $"ABCD"$ 的子序列 | | **常见问题** | 最长公共子串(要求连续匹配) | 最长公共子序列(LCS,允许不连续匹配)[^4] | #### 3. **示例说明** 假设有两个字符串 $A = "ABCBDAB"$ 和 $B = "BDCAB"$: - **子串示例**:$"CAB"$ 是 $A$ 的子串(因为它是连续片段),但不是 $B$ 的子串($B$ 中 $"CAB"$ 不连续)。 - **子序列示例**:$"BCAB"$ 是 $A$ 的子序列(元素顺序为 $B \to C \to A \to B$,跳过了部分元素),也是 $B$ 的子序列(顺序相同)。因此,$"BCAB"$ 是两者的一个公共子序列。 #### 4. **应用场景** - **子序列**:广泛用于比较序列相似度,例如: - 最长公共子序列(LCS)问题,用于DNA序列比对(生物信息学)或文本差异检测(如Git版本控制)[^1][^4]。 - 算法实现中通常使用动态规划,因为子序列允许不连续性,便于优化计算[^2]。 - **子串**:适用于需要精确连续匹配的场景,例如: - 字符串搜索(如KMP算法)或关键词匹配。 - 最长公共子串问题,用于检测抄袭或连续模式识别[^5]。 理解这一区别对于学习算法(如动态规划)至关重要。子序列的灵活性使其在复杂序列分析中更常用,而子串的严格连续性则适合精确匹配任务[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值