最长上升子序列 最长公共子序列 最大字段和 最长不重复子串

1、最长递增子序列

链接:https://www.nowcoder.com/questionTerminal/585d46a1447b4064b749f08c2ab9ce66
来源:牛客网
 

对于一个数字序列,请设计一个复杂度为O(nlogn)的算法,返回该序列的最长上升子序列的长度,这里的子序列定义为这样一个序列U1,U2...,其中Ui < Ui+1,且A[Ui] < A[Ui+1]。

给定一个数字序列A及序列的长度n,请返回最长上升子序列的长度。

测试样例:

[2,1,4,3,1,5,6],7
返回:4

此问题涉及到动态规划的思想,首先,动态规划是简化问题,找到最小的子问题,对于这道题的子问题是

当只有2的时候上升子序列是多少?

当只有2、1的时候上升子序列是多少?

当只有2、1、4的时候上升子序列是多少?

可以发现前后两个问题相互依赖,明白了子问题是什么,接下来看没加一个元素,所产生的状态和选择分别是什么?

状态即:当前最大上升子序列的长度

选择即:新增的元素是否对于他前面某个元素来说是递增的,如果是,那么就选择它加入这个递增子序列队伍中(状态值选最长的子序列+1),不是,那么他就是一个新的子序列,状态值为1。

那么怎么去标记状态呢,我么使用一个一维数组去记录,

 

每一个子问题最后一个新增元素,我们首先去找能不能和之前的元素构成上升序列,如果有多个,那就选长度最大的那个,看看上图是不是也是这样做的。

class AscentSequence {
public:
    int findLongest(vector<int> A, int n) {
        // write code here
        
        vector<int> dp(A.size(),1);
        for(int i=0;i<A.size();++i)
        {
            for(int j=0;j<i;++j)
            {
                if(A[j]<A[i])
                {
                    dp[i]=max(dp[j]+1,dp[i]);
                }
            }
        }
        int ret=INT_MIN;
        for(auto e:dp)
        {
            ret=max(ret,e);
        }
        return ret;
    }
};

2、最长公共子序列

链接:https://www.nowcoder.com/questionTerminal/c996bbb77dd447d681ec6907ccfb488a
来源:牛客网
 

对于两个字符串,请设计一个高效算法,求他们的最长公共子序列的长度,这里的最长公共子序列定义为有两个序列U1,U2,U3...Un和V1,V2,V3...Vn,其中Ui&ltUi+1,Vi&ltVi+1。且A[Ui] == B[Vi]。

给定两个字符串AB,同时给定两个串的长度nm,请返回最长公共子序列的长度。保证两串长度均小于等于300。

测试样例:

"1A2C3D4B56",10,"B1D23CA45B6A",12
返回:6

这个问题就有意思了,直接有两个序列,对于这种问题,直接用二维数组填表,是最容易实现的手段。

一个数组作为行、一个数组作为列。

首先也要划分子问题,比如说

"1"  和 "B1D23CA45B6A"  有多少个最长公共子序列

"1A "和 "B1D23CA45B6A"有多少个最长公共子序列

依次类推去填表:

注意,填表的过程是当只有b 时  和"1A2C3D4B56" 最长公共子序列,b这一行最后一个值就是答案,

下一个只有"b1"时  和"1A2C3D4B56" 最长公共子序列填表的时候不去想状态转移方程,就根据自己的判断,决定当前值是多少,举个栗子,这个过程是正确的心理过程,一点一点的增加子问题,记录当前自己判断的状态值(最长公共子序列长度)

 

这个过程就是肉眼搜索,填入值。

填完之后,寻找状态转移方程,让程序自动去填表,很容易发现,当前值取它上边或者左边较大的值,当两个元素相等的时候,取上边或者左边较大值+1,。

其实这个规律也很容易被理解,就是当前序列新增一个值,选择就是加不加入这个值进入最长公共子序列,条件是,这个值和对比的序列的对应位置值相等,状态无非就是,记录值+1 或者保持不变。

class LCS {
public:
    int findLCS(string A, int n, string B, int m) {
        // write code here
        if(n==0 || m==0)
            return 0;
        vector<vector<int>> m_v(n+1,vector<int>(m+1,0));
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=m;++j)
            {
                if(A[i-1]==B[j-1])
                {
                    m_v[i][j]=m_v[i-1][j-1]+1;
                }
                else
                {
                    m_v[i][j]=max(m_v[i][j-1],m_v[i-1][j]);
                }
            }
        }
        return m_v[n][m];
             
    }
};

3、字符串的最长不重复子序列

比如 "abcdabefghek"   最长不重复长度是8

 https://www.nowcoder.com/questionTerminal/b56799ebfd684fb394bd315e89324fb4

这道题使用滑动窗口比较同意解决,滑动窗口擅长处理一段连续的子串优化的问题,https://blog.csdn.net/weixin_43447989/article/details/106909287

针对这道题目,始终在窗口中保持连续的不重复元素就可以了,做法就是每次加入一个元素,去窗口中遍历寻找有无重复的,如果没有就将当前值加入进去,如果存在重复元素,那肯定就将重复元素和它之前的元素都舍弃了,每加入一个元素求一次窗口的元素大小记录下来,最终就能得到最大的无重复子串。

使用双端队列,可以在两端插入和删除,也是滑动窗口常用的数据结构。

class Solution {
public:
	/**
	 *
	 * @param arr int整型vector the array
	 * @return int整型
	 */
	int maxLength(vector<int>& arr) {
		// write code here
		//使用滑动窗口[2,2,3,4,3]
		
		for (auto &e : arr)
		{
			deal(e);
		}
		return _MAX;
	}
	void deal(int a)
	{
		int flag = -1;
		for (int i = 0; i < de.size(); ++i)
		{
			if (de[i] == a)
			{
				flag = i;
			}
		}
		if (flag != -1)
		{
			for (int i = 0; i <= flag; ++i)
			{
				de.pop_front();
			}
			de.push_back(a);
		}
		else
		{
			de.push_back(a);
		}
		_MAX = max(_MAX, (int)de.size());
	}
	int _MAX = INT_MIN;
	deque<int> de;
};

4、子数组最大子段和

链接:https://www.nowcoder.com/questionTerminal/03d341fb6c9d42debcdd38d82a0a545c
来源:牛客网
 

输入一个整形数组(可能有正数和负数),求数组中连续子数组(最少有一个元素)的最大和。要求时间复杂度为O(n)。

 

输入描述:

【重要】第一行为数组的长度N(N>=1)

接下来N行,每行一个数,代表数组的N个元素

-2 4 3 -1 5 7 -5

此问题适合使用动态规划来做,分解子问题

-2时最大和

-2 4 时最大和

-2 4 3 时最大和

........

我们发现序列中新增一个值它的状态和选择如下:选择:加入还是不加入子序列中,

1、加入的条件是此值加入以后不能使临时的最大值记录变成负值,因为正值它还能对整体最大值有增大效果,负值就不行了。换句话说就是当前值+之前记录的和不能小于当前值。如果出现那就将记录的和更新为当前值。

每次新处理一个元素都去将当前值和最大值比较,更新最大值

int FindMax(int* num, int size)
 {
 
	if (size <= 0)
	{
		return 0;
	}
	int max = num[0];
	int tmp = num[0];
	for (int i = 1; i < size; i++)
	{
		tmp = tmp + num[i];
		if (num[i] > tmp)
			tmp = num[i];
		if (tmp > max)
			max = tmp;
	}
	return max;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值