[Monotonic stack] LeetCode321: Maximum number of splices

Recommended by the author

[Dynamic Programming] [Breadth-First Search] LeetCode: 2617 The minimum number of cells visited in the grid graph

Knowledge points involved in this article

monotonic stack

topic

Given two arrays of length m and n respectively, their elements are composed of 0-9, representing the numbers in each bit of the two natural numbers. Now select k (k <= m + n) numbers from these two arrays and splice them into a new number. The numbers taken from the same array are required to maintain their relative order in the original array.
Find the maximum number that satisfies this condition. The result is an array of length k representing the maximum number.
Note: Please optimize the time and space complexity of your algorithm as much as possible.
Example 1:
Input:
nums1 = [3, 4, 6, 5]
nums2 = [9, 1, 2, 5, 8, 3]
k = 5
Output:
[ 9, 8, 6, 5, 3]
Example 2:
Input:
nums1 = [6, 7]< /span> nums1 = [3, 9]< /span> [9, 8, 9] Output: k = 3 nums2 = [8, 9] Input: Example 3: [ 6, 7, 6, 0, 4] Output: k = 5
nums2 = [6, 0, 4]









monotonic stack

Time complexity: O(k(m+n+max(n,m)*max(n,m))). Enumerating m and n, the time complexity is O(k). For any combination of m and n, the processing is divided into the following four steps: 1. Calculate the maximum lexicographic subsequence v1 of length m of nums1. Second, calculate the maximum lexicographic subsequence v2 of length n in nums2. Third, merge v1 and v2 into cur. Fourth, compare the sizes of cur and vRet.

Corresponding to any m,n only need to consider the maximum dictionary order

Assume that the subsequence composed of elements from nums1 in the final result is not in maximum dictionary order, and is replaced by the maximum dictionary sequence. It also conforms to the meaning of the question, and the dictionary order remains unchanged or becomes larger.

Maximum lexicographic order of length len

vVet[0,i] records the maximum lexicographic subsequence of length i+1. You can think of vRet as a queue. When the new element is larger than the tail element of the queue, replacing the tail element will form a new maximum subsequence. You can always replace the last element of the guide queue to be greater than or equal to the current element or the queue is empty. The problem is that the length of vRet may not be empty. The solution:

If all the remaining numbers are added to the queue after being dequeued, the length of vRet cannot be made greater than or equal to k. Then don’t leave the team
If vRet.size()>=k Then don’t join the team

merge v1v2

We regard v1 and v2 as queues, and we can only take the head of the queue each time. Take the larger value of the two team leaders each time. If they are equal, then:
Assume that v1 and v2 have the same prefix vPre, and their length is len. Assume that vPre of v1 is followed by c. Assume The d after v2 and vPre. Select v1 or v2, the first len ​​characters can be the same. Select v1, the len+1th character can be c, but not d. Select v2, the len+1th character is d, not c. Obviously, if c is large, choose v1; if d is large, choose v2. Let's consider a special case:

c and d do not exist v1 and v2 are exactly the same, and the effect of selecting v1 and v2 is the same
c exists, d does not exist In order to select c for len+1, select v1
d exists, c does not exist In order to select d for len+1, select v2

To sum up the above: Just choose the one with the largest lexicographic order, and use lexicographical_compare to simplify the code.

code

core code

class Solution {
    
    
public:
	vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
    
    
		vector<int> vRet(k);
		for (int len1 = 0; len1 <= min((int)nums1.size(), k); len1++)
		{
    
    
			const int len2 = k - len1;
			if (len2 > nums2.size())
			{
    
    
				continue;
			}
			if (len2 < 0)
			{
    
    
				break;
			}
			vector<int> v1 = TopMax(nums1, len1);
			vector<int> v2 = TopMax(nums2, len2);
			vector<int> cur;
			int i1 = 0, i2 = 0;
			while ((i1 < v1.size()) && (i2 < v2.size()))
			{
    
    
				auto Cmp = [&v1,&v2](int i1,int i2)
				{
    
    		
					while((i1<v1.size())&&(i2 < v2.size()))
					{
    
    
						const int iCmp = v1[i1] - v2[i2];
						if (iCmp > 0 )
						{
    
    
							return true;;
						}
						else if (iCmp < 0)
						{
    
    
							return false;
						}
						i1++;
						i2++;
					}				
					return i1 < v1.size();
				};
				if (Cmp(i1,i2))
				{
    
    
					cur.emplace_back(v1[i1++]);
				}
				else
				{
    
    
					cur.emplace_back(v2[i2++]);
				}
			}
			cur.insert(cur.end(), v1.begin() + i1, v1.end());
			cur.insert(cur.end(), v2.begin() + i2, v2.end());
			vRet = max(cur, vRet);
		}

		return vRet;
	}
	vector<int> TopMax(const vector<int>& nums, int k)
	{
    
    
		vector<int> ret;
		for (int i = 0; i < nums.size(); i++)
		{
    
    
			while (ret.size() && (ret.back() < nums[i]) && (ret.size() + nums.size() - i > k))
			{
    
    
				ret.pop_back();
			}
			if (ret.size() < k)
			{
    
    
				ret.emplace_back(nums[i]);
			}
		}
		return ret;
	}
};

test case

template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
    
    
	if (v1.size() != v2.size())
	{
    
    
		assert(false);
		return;
	}
	for (int i = 0; i < v1.size(); i++)
	{
    
    
		assert(v1[i] == v2[i]);
	}
}

template<class T>
void Assert(const T& t1, const T& t2)
{
    
    
	assert(t1 == t2);
}

int main()
{
    
    
	vector<int> nums1,  nums2;
	int k;

	{
    
    
		Solution slu;		
		nums1 = {
    
     3, 4, 6, 5 };
		nums2 = {
    
     9, 1, 2, 5, 8, 3 };
		k = 5;
		auto res = slu.maxNumber(nums1, nums2, k);
		Assert(vector<int>{
    
    9, 8, 6, 5, 3}, res);
	}
	{
    
    
		Solution slu;
		nums1 = {
    
     6, 7 };
		nums2 = {
    
     6, 0, 4 };
		k = 5;
		auto res = slu.maxNumber(nums1, nums2, k);
		Assert(vector<int>{
    
    6, 7, 6, 0, 4}, res);
	}
	{
    
    
		Solution slu;
		nums1 = {
    
     3, 9 };
		nums2 = {
    
     8,9 };
		k = 3;
		auto res = slu.maxNumber(nums1, nums2, k);
		Assert(vector<int>{
    
    9, 8, 9}, res);
	}
}

Simplified code

class Solution {
    
    
public:
	vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
    
    
		vector<int> vRet(k);
		for (int len1 = 0; len1 <= min((int)nums1.size(), k); len1++)
		{
    
    
			const int len2 = k - len1;
			if (len2 > nums2.size())
			{
    
    
				continue;
			}
			if (len2 < 0)
			{
    
    
				break;
			}
			vector<int> v1 = TopMax(nums1, len1);
			vector<int> v2 = TopMax(nums2, len2);
			vector<int> cur;
			auto it1 = v1.begin();
			auto it2 = v2.begin();
			for (;(it1 != v1.end()) && (it2 != v2.end());)
			{
    
    				
				if (lexicographical_compare(it1,v1.end(),it2,v2.end()))
				{
    
    
					cur.emplace_back(*it2++);
				}
				else
				{
    
    
					cur.emplace_back(*it1++);
				}		
			}
			cur.insert(cur.end(), it1, v1.end());
			cur.insert(cur.end(), it2, v2.end());
			vRet = max(cur, vRet);
		}
		return vRet;
	}
	vector<int> TopMax(const vector<int>& nums, int k)
	{
    
    
		vector<int> ret;
		for (int i = 0; i < nums.size(); i++)
		{
    
    
			while (ret.size() && (ret.back() < nums[i]) && (ret.size() + nums.size() - i > k))
			{
    
    
				ret.pop_back();
			}
			if (ret.size() < k)
			{
    
    
				ret.emplace_back(nums[i]);
			}
		}
		return ret;
	}
};

March 2023

class Solution {
public:
vector maxNumber(vector& nums1, vector& nums2, int k) {
vector vRet;
for (int iLeft = 0; iLeft <= k; iLeft++)
{
const vector v1 = maxNumber(nums1, iLeft);
const vector v2 = maxNumber(nums2, k - iLeft);
if (v1.size() + v2.size() != k)
{
continue;
}
vector nums;
auto it1 = v1.begin();
auto it2 = v2.begin();
while ((it1 != v1.end() ) && (it2 != v2.end() ))
{
if (*it1 > *it2 )
{
nums.push_back(*it1);
it1++;
}
else if(*it1 < *it2)
{
nums.push_back(*it2);
it2++;
}
else
{
if (Less(it1, v1.end(), it2, v2.end()))
{
nums.push_back(*it2);
it2++;
}
else
{
nums.push_back(*it1);
it1++;
}
}
}
std::copy(it1, v1.end(), std::back_inserter(nums));
std::copy(it2, v2.end(), std::back_inserter(nums));
if ((vRet.size() == 0) || Less(vRet,nums))
{
vRet.swap(nums);
}
}
return vRet;
}
template
bool Less(IT itBegin1, IT itEnd1, IT itBegin2, IT itEnd2)
{
while ((itBegin1 != itEnd1) && (itBegin2 != itEnd2))
{
if (*itBegin1 < *itBegin2)
{
return true;
}
else if (*itBegin1 > *itBegin2)
{
return false;
}
itBegin1++;
itBegin2++;
}
return itBegin1 == itEnd1;
}
bool Less(const vector& v1, const vector& v2)
{
for (int i = 0; i < v1.size(); i++)
{
if (v1[i] < v2[i])
{
return true;
}
else if (v1[i] > v2[i])
{
return false;
}
}
return false;
}
vector maxNumber(const vector& nums, int k)
{
vector ret;
for (int i = 0; i < nums.size(); i++)
{
const int& n = nums[i];
while (ret.size() && (n > ret.back()) && ((ret.size() + nums.size() - i - 1) >= k))
{
ret.pop_back();
}
if (ret.size() < k)
{
ret.push_back(n);
}
}
return ret;
}
};

August 2023

template<class T = int,class _Pr = std::less >
class CTopK
{
public:
CTopK(int k):m_iMinNum(k)
{
}
void Do(vector& m_v,T* begin, int num)
{
for (; num ; begin++,num–)
{
while (m_v.size() && _Pr()( *begin, m_v.back()) && (m_iMinNum - m_v.size()+1 <= num))
{
m_v.pop_back();
}
if (m_v.size() < m_iMinNum)
{
m_v.push_back(*begin);
}
}
}
protected:
const int m_iMinNum;
};

class Solution {
public:
vector maxNumber(vector& nums1, vector& nums2, int k) {
CTopK<int,std::greater> tok(k);
vector<vector> vNums1(k + 1), vNums2(k + 1);
tok.Do(vNums1[k], nums1.data(), nums1.size());
tok.Do(vNums2[k], nums2.data(), nums2.size());
for (int i = k - 1; i >0; i–)
{
CTopK<int, std::greater> tok(i);
tok.Do(vNums1[i], vNums1[i + 1].data(), vNums1[i + 1].size());
tok.Do(vNums2[i], vNums2[i + 1].data(), vNums2[i + 1].size());
}
vector vRet(k);
for (int i = max(0,k-(int)nums2.size()); i <= min(k,(int)nums1.size()); i++)
{
const auto& v1 = vNums1[i];
const auto& v2 = vNums2[k - i];
vector cur;
auto it = v1.begin();
auto ij = v2.begin();
while ((it != v1.end()) || (ij != v2.end()))
{
bool b = lexicographical_compare(it, v1.end(), ij, v2.end());
if (b)
{
cur.emplace_back((ij++));
}
else
{
cur.emplace_back(
(it++));
}
}
if (cur > vRet)
{
vRet.swap(cur);
}
}
return vRet;
}
};

Further reading

video course

Effective learning: clear goals, timely feedback, stretching zone (appropriate difficulty), you can learn simple courses first, please go to CSDN Academy and listen to the explanation of the Baiyin instructor (that is, I).
https://edu.csdn.net/course/detail/38771

how fast do you want

The battle is about to begin. To share the worries of the boss, please learn C# onboarding training, C++ onboarding training and other courses
https://edu.csdn.net/lecturer/6176

Related

download

If you want to know the learning algorithm from a superior position, please download the doc version of "Xi Que Complete Book of Algorithms"
https://download.csdn.net/download/he_zhidan/88348653

What I want to say to everyone
It is a good wish to be happy when you hear defects, to find problems early, correct them early, and save money for the boss.
Zimozi said: Nothing has an end or a beginning, and there is no business and many undertakings. That is what we often say: professional people do professional things.
If the program is a one-stop process, then the algorithm is its key point

test environment

Operating system: win7 Development environment: VS2019 C++17
Or operating system: win10 Development environment: VS2022 C++17
Unless otherwise specified, this algorithm is implemented in **C++**.

Guess you like

Origin blog.csdn.net/he_zhidan/article/details/135031953