最长重复子数组和最长公共子序列的动态规划算法及其区别

最长重复子数组

要求元素在原数组中连续

例如

  • 1 2 3 6 7
  • 5 8 1 2 3

此时最长重复子数组就是1 2 3, 长度为3

下面是解法思路:

首先用暴力解法思考,按照每一个元素位依次比较,时间复杂度为n3,包括大量重复计算。将暴力解法的每一步记录在二维数组中, 省略了重复比较,以上例作为参考,二维数组构造如下:

  1 2 3 6 7
5 0 0 0 0 0
8 0 0 0 0 0
1 1 0 0 0 0
2 0 2 0 0 0
3 0 0 3 0 0

从二维数组中可以看出最长子数组为3

推导过程:

令数组一为str1,数组二为str2,则 dp[ i ] [ j ]的数值意义是,以数组str1[ i ]结尾和数组二以str2[ j ]结尾的子数组中重复的数组长度最长为多少。
每次数组移动有两种情况

  • 这两个子数组结尾相同
  • 这两个子数组结尾不同

如果是相同则在dp[ i - 1 ] [ j - 1 ]的基础上加一,否则归零,因为不接受不连续的相同子数组,这里已经截断。

代码


#include<stdio.h>
#include<stdlib.h>


//最长重复子数组
int main() {
	char str1[30];
	char str2[30];
	scanf("%s%s", str1, str2);
	int arr[10][10];
	int count1 = strlen(str1);
	int count2 = strlen(str2);
	for (int i = 0; i < count1; i ++) {//初始化第一行
		if (str1[i] == str2[0]) {
			arr[0][i] = 1;
		}
		else {
			arr[0][i] = 0;	
		}
	}
	for (int i = 0; i < count2; i ++) {//初始化第一列
		if (str2[i] == str1[0]) {
			arr[i][0] = 1;
		}
		else {
			arr[i][0] = 0;
		}
	}
	for (int i = 1; i < count2; i ++) {//arr[i][j] = arr[i - 1][j - 1] OR 0
		for (int j = 1; j < count1; j ++) {
			if (str1[j] == str2[i]) {
				arr[i][j] = arr[i - 1][j - 1] + 1;
			}
			else {
				arr[i][j] = 0;
			}
		}
	}
	int result = 0;
	for (int i = 0; i < count2; i ++) {
		for (int j = 0; j < count1; j ++) {
			if (arr[i][j] > result) {
				result = arr[i][j];
			}
		}
	}
	printf("%d", result);
	return 0;
}

最长公共子序列

要求元素在原数组中不连续

例如

  • 1 2 3 6 7 4
  • 5 8 1 2 3 4

此时最长重复子数组就是1 2 3 4, 长度为4

下面是解法思路:

首先用暴力解法思考,按照每一个元素位依次比较,时间复杂度为n3,包括大量重复计算。将暴力解法的每一步记录在二维数组中, 省略了重复比较,以上例作为参考,二维数组构造如下:

  1 2 3 6 7 4
5 0 0 0 0 0 0
8 0 0 0 0 0 0
1 1 1 1 1 1 1
2 1 2 2 2 2 2
3 1 2 3 3 3 3
4 1 2 3 3 3 4

从二维数组中可以看出最长子数组为4

推导过程:

令数组一为str1,数组二为str2,则 dp[ i ] [ j ]的数值意义是,以数组str1[ i ]结尾和数组二以str2[ j ]结尾的子数组中重复的数组长度最长为多少。
每次数组移动有两种情况

  • 这两个子数组结尾相同
  • 这两个子数组结尾不同

如果是相同则在dp[ i - 1 ] [ j - 1 ]的基础上加一,否则变为 MAX {dp[ i ] [ j - 1], dp[ i - 1] [ j ] },因为接收不连续的子序列,最大值是继承的。

代码

#include<stdio.h>
#include<stdlib.h>

int max(int x, int y) {
	if (x > y) {
		return x;
	}
	else {
		return y;
	}
}



int main() {
	char str1[30];
	char str2[30];
	scanf("%s%s", str1, str2);
	int count1 = strlen(str1);
	int count2 = strlen(str2);
	int  arr[30][30];
	if (str1[0] == str2[0]) {
		arr[0][0] = 1;
	}
	else {
		arr[0][0] = 0;
	}
	for (int i = 1; i < count1; i ++) {//初始化第一行
		if (str1[i] == str2[0]) {
			arr[0][i] = 1;
		}
		else {
			arr[0][i] = max(arr[0][i - 1], 0);
		}
	}
	for (int i = 1; i < count2; i ++) {//初始化第一列
		if (str2[i] == str1[0]) {
			arr[i][0] = 1;
		}
		else {
			arr[i][0] = max(arr[i - 1][0], 0);
		}
	}
	for (int i = 1; i < count2; i ++) {//推导矩阵
		for (int j = 1; j <count1; j ++) {
			if (str1[j] == str2[i]) {
				arr[i][j] = arr[i - 1][j - 1] + 1;
			}
			else {
				arr[i][j] = max(arr[i - 1][j], arr[i][j - 1]);
			}
		}
	}
	printf("%d", arr[count2 - 1][count1 - 1]);
	return 0;
}

两个算法的区别

对于最长重复子数组,结果需要连续,所以遇到不一样的字符时需要从0开始。对于后者仅需要相同次序,所以每一个字符继承前端结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值