算法学习——动态规划之子序列问题

目录

一、最长递增子序列

1、O(n^2)做法

二、最长连续递增子序列

三、最长重复子数组

1、方法一

2、方法二

3、滚动数组优化

四、最长公共子序列

五、最大子数组和

六、不同的子序列

七、两个字符串的删除操作

八、编辑距离

九、回文子串

十、最长回文子序列


一、最长递增子序列

        原题力扣300        . - 力扣(LeetCode)

        定义 dp[i] 为以第 i 个元素为结尾的最长递增子序列长度

1、O(n^2)做法

int longestIncreasingSubsequence(vector<int>& nums)
{
	int n = nums.size();
	vector<int> dp(n, 1);
	for (int i = 1; i < n; i++)
	{
		for (int j = 0; j < i; j++)
		{
			if (nums[i] > nums[j])
			{
				dp[i] = max(dp[i], dp[j] + 1);
			}
		}
	}
	return *max_element(dp.begin(), dp.end());
}

二、最长连续递增子序列

        原题力扣674        . - 力扣(LeetCode)

        定义 dp[i] 为以第 i 个元素为结尾的最长连续递增子序列长度

int findLengthOfLCIS(vector<int>& nums)
{
	int n = nums.size();
	vector<int> dp(n, 1);
	for (int i = 1; i < n; i++)
	{
		if (nums[i] > nums[i - 1])
		{
			dp[i] = dp[i - 1] + 1;
		}
	}
	return *max_element(dp.begin(), dp.end());
}

三、最长重复子数组

        原题力扣718        . - 力扣(LeetCode)

1、方法一

         定义 dp[i][j] :分别以 i-1,j-1 为结尾的nums1,nums2最长重复子数组的长度

int findLength(vector<int>& nums1, vector<int>& nums2)
{
	vector<vector<int>> dp(nums1.size() + 1, vector<int>(nums2.size() + 1));
	for (int i = 0; i <= nums1.size(); i++)
	{
		dp[i][0] = 0;
	}
	for (int j = 0; j <= nums2.size(); j++)
	{
		dp[0][j] = 0;
	}
	int ans = 0;
	for (int i = 1; i <= nums1.size(); i++)
	{
		for (int j = 1; j <= nums2.size(); j++)
		{
			if (nums1[i - 1] == nums2[j - 1])
			{
				dp[i][j] = dp[i - 1][j - 1] + 1;
			}
			ans = max(ans, dp[i][j]);
		}
	}
	return ans;
}

2、方法二

        定义 dp[i][j] :分别以 i,j 为结尾的nums1,nums2最长重复子数组的长度

int findLength(vector<int>& nums1, vector<int>& nums2)
{
	vector<vector<int>> dp(nums1.size(), vector<int>(nums2.size()));
	for (int i = 0; i < nums1.size(); i++)
	{
		if (nums1[i] == nums2[0])
		{
			dp[i][0] = 1;
		}
	}
	for (int j = 0; j < nums2.size(); j++)
	{
		if (nums1[0] == nums2[j])
		{
			dp[0][j] = 1;
		}
	}
	int ans = 0;
	for (int i = 0; i < nums1.size(); i++)
	{
		for (int j = 0; j < nums2.size(); j++)
		{
			if (i > 0 && j > 0 && nums1[i] == nums2[j])
			{
				dp[i][j] = dp[i - 1][j - 1] + 1;
			}
			ans = max(ans, dp[i][j]);
		}
	}
	return ans;
}

3、滚动数组优化

int findLength(vector<int>& nums1, vector<int>& nums2)
{
	vector<int> dp(nums2.size() + 1, 0);
	int ans = 0;
	for (int i = 1; i <= nums1.size(); i++)
	{
		for (int j = nums2.size(); j > 0; j--)
		{
			if (nums1[i - 1] == nums2[j - 1])
			{
				dp[j] = dp[j - 1] + 1;
			}
			else
			{
				dp[j] = 0; // 注意这里不相等的时候要有赋0的操作
			}
			ans = max(ans, dp[j]);
		}
	}
	return ans;
}

四、最长公共子序列

        原题力扣1143        . - 力扣(LeetCode)

        定义 dp[i][j] :分别以 i-1,j-1 为结尾的 text1,text2 字符串的最长公共子序列长度

        注意与上题比较

        变形:力扣1035        . - 力扣(LeetCode)        代码一模一样

int longestCommonSubsequence(string text1, string text2)
{
	vector<vector<int>> dp(text1.size() + 1, vector<int>(text2.size() + 1));
	for (int i = 1; i <= text1.size(); i++)
	{
		for (int j = 1; j <= text2.size(); j++)
		{
			if (text1[i - 1] == text2[j - 1])
			{
				dp[i][j] = dp[i - 1][j - 1] + 1;
			}
			else
			{
				dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
			}
		}
	}
	return dp[text1.size()][text2.size()];
}

        变形:力扣392        . - 力扣(LeetCode)

bool isSubsequence(string s, string t)
{
	vector<vector<int>> dp(s.size() + 1, vector<int>(t.size() + 1, 0));
	for (int i = 1; i <= s.size(); i++)
	{
		for (int j = 1; j <= t.size(); j++)
		{
			if (s[i - 1] == t[j - 1])
			{
				dp[i][j] = dp[i - 1][j - 1] + 1;
			}
			else
			{
				dp[i][j] = dp[i][j - 1];  // 只能从 t 中删除元素
			}
		}
	}
	return dp[s.size()][t.size()] == s.size();
}

五、最大子数组和

        原题力扣53        . - 力扣(LeetCode)

        定义 dp[i]:以 nums[i] 为结尾的最大子数组和

int maxSubArray(vector<int>& nums)
{
	int n = nums.size();
	vector<int> dp(n, 0);
	dp[0] = nums[0];
	for (int i = 1; i < n; i++)
	{
		dp[i] = max(dp[i - 1] + nums[i], nums[i]);
	}
	return *max_element(dp.begin(), dp.end());
}

六、不同的子序列

        原题力扣115        . - 力扣(LeetCode)

        定义 dp[i][j]:以 i-1 为结尾的 s 中含有以 j-1 为结尾的 t 的个数

int numDistinct(string s, string t)
{
	vector<vector<int>> dp(s.size() + 1, vector<int>(t.size() + 1, 0));
	// 注意初始化,s 中删除任意元素后变成空串的个数只有 1 个
    for (int i = 0; i <= s.size(); i++)
	{
		dp[i][0] = 1;
	}
	for (int i = 1; i <= s.size(); i++)
	{
		for (int j = 1; j <= t.size(); j++)
		{
			if (s[i - 1] == t[j - 1])
			{
				dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
			}
			else
			{
				dp[i][j] = dp[i - 1][j];
			}
		}
	}
	return dp[s.size()][t.size()];
}

七、两个字符串的删除操作

        原题力扣583        . - 力扣(LeetCode)

        定义 dp[i][j]:以 i-1 为结尾的 word1,以 j-1 为结尾的 word2 的字符串,删除字符使它们相同的最小操作次数

int minDistance(string word1, string word2)
{
	vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));
	for (int i = 0; i <= word1.size(); i++)
	{
		dp[i][0] = i;
	}
	for (int j = 0; j <= word2.size(); j++)
	{
		dp[0][j] = j;
	}
	for (int i = 1; i <= word1.size(); i++)
	{
		for (int j = 1; j <= word2.size(); j++)
		{
			if (word1[i - 1] == word2[j - 1])
			{
				dp[i][j] = dp[i - 1][j - 1];
			}
			else
			{
                // 三种情况:删除word1[i-1]、删除word2[j-1]、同时删除word1和word2
				dp[i][j] = min(dp[i - 1][j] + 1, min(dp[i][j - 1] + 1, dp[i - 1][j - 1] + 2));
			}
		}
	}
	return dp[word1.size()][word2.size()];
}

八、编辑距离

        原题力扣72        . - 力扣(LeetCode)

int minDistance(string word1, string word2)
{
	vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));
	for (int i = 0; i <= word1.size(); i++)
	{
		dp[i][0] = i;
	}
	for (int j = 0; j <= word2.size(); j++)
	{
		dp[0][j] = j;
	}
	for (int i = 1; i <= word1.size(); i++)
	{
		for (int j = 1; j <= word2.size(); j++)
		{
			if (word1[i - 1] == word2[j - 1])
			{
				dp[i][j] = dp[i - 1][j - 1];
			}
			else
			{
                // 增删操作相同、替换dp[i - 1][j - 1] + 1
				dp[i][j] = min(dp[i - 1][j] + 1, min(dp[i][j - 1] + 1, dp[i - 1][j - 1] + 1));
			}
		}
	}
	return dp[word1.size()][word2.size()];
}

九、回文子串

        原题力扣647        . - 力扣(LeetCode)

        定义 dp[i][j]:[i...j]的子串中回文串的数量

int countSubstrings(string s)
{
	vector<vector<bool>> dp(s.size(), vector<bool>(s.size(), false));
	int ans = 0;
    // 注意遍历顺序,由递推公式知,从下到上,从左到右
	for (int i = s.size() - 1; i >= 0; i--)
	{
		for (int j = i; j < s.size(); j++)
		{
			if (s[i] == s[j])
			{
				if (j - i <= 1)
				{
					dp[i][j] = true;
					ans++;
				}
				else if (dp[i + 1][j - 1] == true)
				{
					dp[i][j] = true;
					ans++;
				}
			}
		}
	}
	return ans;
}

十、最长回文子序列

        原题力扣516        . - 力扣(LeetCode)

        定义 dp[i][j]:[i...j]范围的子串的最长回文子序列的长度

int longestPalindromeSubseq(string s)
{
	vector<vector<int>> dp(s.size(), vector<int>(s.size(), 0));
	for (int i = 0; i < s.size(); i++)
	{
		dp[i][i] = 1;
	}
	for (int i = s.size() - 1; i >= 0; i--)
	{
		for (int j = i + 1; j < s.size(); j++)
		{
			if (s[i] == s[j])
			{
				dp[i][j] = dp[i + 1][j - 1] + 2;
			}
			else
			{
				dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
			}
		}
	}
	return dp[0][s.size() - 1];
}

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值