【例题】【动态规划】最长公共子序列

经典模板

例题:1143. 最长公共子序列

求两个字符串的最长公共子序列长度。

d p [ i + 1 ] [ j + 1 ] = { d p [ i ] [ j ] + 1  if  s t r 1 [ i ] = = s t r 2 [ j ] M a x ( d p [ i ] [ j + 1 ] , d p [ i + 1 ] [ j ] )  if  s t r 1 [ i ] ! = s t r 2 [ j ] dp[i+1][j+1]=\begin{cases} dp[i][j] + 1 & \text{ if } str1[i] == str2[j]\\ Max(dp[i][j+1],dp[i+1][j]) & \text{ if } str1[i] != str2[j] \end{cases} dp[i+1][j+1]={dp[i][j]+1Max(dp[i][j+1],dp[i+1][j]) if str1[i]==str2[j] if str1[i]!=str2[j]
dp[i+1][j+1]代表str1的0到i位置子串 和 str2的0到j位置的子串最长公共子序列

str1[i]和str2[j]如果相等,则其最长公共子序列长度为str[i-1]与str[j-1]最长公共子序列长度加一。否则为
str1[i]与str2[j-1] 或
str1[i-1]与str2[j]最长公共子序列最大值

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int[][] dp = new int[text1.length()+1][text2.length()+1];
        for (int i = 0;i<text1.length();i++){
            for(int j = 0; j < text2.length();j++){
                if (text1.charAt(i) == text2.charAt(j)) {
                    dp[i+1][j+1] = dp[i][j] + 1;
                } else {
                    dp[i+1][j+1] = Math.max(dp[i][j+1],dp[i+1][j]);
                }
            }
        }
        return dp[text1.length()][text2.length()];
    }
}

变形例题

回文串最长

给定一个字符串s,你可以从中删除一些字符,使得剩下的串是一个回文串。如何删除才能使得回文串最长呢?
输出需要删除的字符个数。
输入描述:
输入数据有多组,每组包含一个字符串s,且保证:1<=s.length<=1000.
输出描述:
对于每组数据,输出一个整数,代表最少需要删除的字符个数。

动态规划法

  • 字符串反转,求两个字符串的最长公共子序列,总长度减去就行
#include<stdlib.h>
#include<stdio.h>
#include<string>
#include<iostream>
using namespace std;
#define N 100
int dp[N][N];
string s1;
string s2;

int max(int a, int b){
	return (a>b ? a : b);
}
int solve(){
	int len = s1.size();
	for (int i = 0; i<len; i++){
		for (int j = 0; j<len; j++){
			if (s1[i] == s2[j]){
				dp[i + 1][j + 1] = dp[i][j] + 1;
			}
			else{
				dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]);
			}
		}
	}
	return len - dp[len][len];
}
int main(){

	while (cin >> s1){
		for (int i = 0; i<s1.size(); i++){
			for (int j = 0; j<s1.size(); j++)dp[i][j] = 0;
		}
		s2 = "";
		for (int i = 0; i<s1.size(); i++){
			s2 = s2 + s1[s1.size() - 1 - i];
		}
		int res = solve();
		cout << res << endl;
	}
}

暴力法

假设初始字符串长度为0,逐渐往字符串后加字符。
arr[i]为从字符串str[i]开始到结束最长的回文串长度

#include<string>
#include<string.h>
#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#include<algorithm>
using namespace std;
#define MAX(a,b) (a>b?a:b)
#define N 1000+7

int main(){
	string str;
	
	while (cin >> str){
		int arr[N];
		memset(arr, 0, sizeof(arr));
		int len = str.size();
		for (int i = 1; i < len; i++){
		//每添加一个字符,计算一次新字符串里最长的回文串的长度
			for (int j = 0; j < i; j++){
				if (str[i] == str[j]){
				//更新数组
					for (int k = 0; k <= j; k++){
						if (arr[k] < arr[j + 1] + 2)
							arr[k] = arr[j + 1] + 2;
					}
				}
			}
			//从str[i[到str[i]的字符串的最长回文串为自身,长度为1
			arr[i] = 1;
		}
		cout << len - arr[0] << endl;
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值