[개인 노트] Niuke 주제: NC91 가장 긴 오름차순 하위 시퀀스(3)

설명
길이가 n인 배열 arr이 주어지면 arr의 가장 긴 오름차순 하위 시퀀스를 출력합니다. (답이 여러 개인 경우 수치 기준 비교를 위해 사전식 순서가 가장 작은 것을 출력해 주십시오. (참고: 단일 문자의 ASCII 코드 값과 다름) 1000000000
요구사항
: 공간복잡도 O(n), 시간복잡도 O(nlogn)

예: 입력: [2,1,5,3,6,4,8,9,7]
반환: [1,3,4,8,9]

입력: [1,2,8,6,4]
반환값: [1,2,4]
설명: (1, 2, 8), (1, 2, 6) , (1) 3개의 가장 길게 증가하는 하위 시퀀스가 ​​있습니다. , 2, 4), 그 중 세 번째가 수치비교를 위한 사전순으로 가장 작으므로 답은 (1, 2, 4)이다.

문제 해결 아이디어 (Niuke 및 기타 답변 참조):
탐욕 + 이분법을 사용하여
꼬리가 더 길고 꼬리를 형성한 요소가 더 작을 수 있기를 탐욕스럽게 희망하므로 꼬리에서 첫 번째 것을 찾으십시오. 보다 큼 교체가 없으면 길이를 업데이트해야 함을 의미합니다
예: 다음 시퀀스가 ​​있습니다 A[ ] = 1 4 7 2 5 9 10 3, LIS의 길이를 찾습니다.
A[1] = 1, B[1]에 1 넣기, 이때 B[1] = 1, B[ ] = {1}, len = 1 A[2] = 4, B[2 ]에 4
넣기 , 이때 B[2] = 4, B[ ] = {1,4}, len = 2
A[3] = 7, B[3]에 7을 넣으면 이때 B[3] = 7, B [ ] = {1,4,7}, len = 3
A[4] = 2, 2는 4보다 작기 때문에 B[2]의 4를 2로 바꾸면 B[ ] = {1,2 ,7 }, len = 3
A[5] = 5, 5는 7보다 작기 때문에 B[3]의 7을 5로 바꾼 다음 B[ ] = {1,2,5}, len = 3 A[6
] = 9, B[4]에 9를 넣음 이때 B[4] = 9, B[ ] = {1,2,5,9}, len = 4
A[7] = 10, B에 10을 넣음 [5], 이때 B[5] = 10, B[ ] = {1,2,5,9,10}, len = 5
A[8] = 3, 3이 5보다 작기 때문에 5로 교체 in B[3] with 3. 이때 B[ ] = {1,2,3,9,10}, len = 5,
  최종적으로 LIS 길이는 5이다. 하지만! ! 1 2 3 9 10 여기서 올바른 가장 긴 오름차순 하위 시퀀스가 ​​아닙니다. B 시퀀스는 반드시 가장 긴 오름차순 하위 시퀀스를 나타내는 것은 아니며 가장 긴 하위 시퀀스의 길이에 해당하는 정렬된 최소 시퀀스만 나타냅니다.
(위는 https://blog.csdn.net/lxt_Lucia/article/details/81206439 에서 가져온 것입니다 .) 이 질문은 가장 작은 사전식 순서의 하위 시퀀스를 가져와야 하므로 위의 내용을
기반으로 수정합니다.
i]는 현재 요소 arr[i]가 마지막에 가장 길게 증가하는 서브 시퀀스의 길이라는 것을 의미하며,
curLen[i]에 따르면 최대 길이부터 역순으로 즉, 다음의 조건을 만족하는 요소부터 순회한다. curLen[i] == ans는 길이 ans의 사전식 순서가 가장 작은 스키마입니다.

class Solution {
    
    
public:
    /**
     * retrun the longest increasing subsequence
     * @param arr int整型vector the array
     * @return int整型vector
     */
    vector<int> LIS(vector<int>& arr) {
    
    
        // write code here
        vector<int> tail(arr.size());  // 贪心+二分得到的子序列
        vector<int> curlen(arr.size()); // curlen[i]表示以arr[i]为结尾的元素对应的最长上升子序列长度
        // 遍历arr
        int tail_size = 0; // 因为tail初始化了一个固定的大小,所以得有一个表示tail当前大小的数
        int left, right, mid;
        for(int i=0; i<arr.size(); i++){
    
    
            // 二分法从tail中找第一个比arr[i]大的数
            left=0;
            right = tail_size;
            while(left<right){
    
    
                mid = left + (right-left)/2;
                if(tail[mid] >= arr[i])
                    right = mid;
                else
                    left = mid + 1;
            }
            // 若找到,找到后用arr[i]将那个较大的数替换掉;
            // 若找不到,说明tail所有数都比arr[i]小,则把arr[i]加入末尾
            tail[left] = arr[i]; 
            curlen[i] = left + 1;
            if(tail_size==left) // 找不到的情况,tail大小+1
                tail_size++;
        }
        /*
		eg:[2,1,5,3,6,4,8,9,7]
		arr[0]:2, tail:[2], curLen[0]:1
		arr[1]:1, tail:[1], curLen[1]:1
		arr[2]:5, tail:[1,5], curLen[2]:2
		arr[3]:3, tail:[1,3], curLen[3]:2
		arr[4]:6, tail:[1,3,6], curLen[4]:3
		arr[5]:4, tail:[1,3,4], curLen[5]:3
		arr[6]:8, tail:[1,3,4,8], curLen[6]:4
		arr[7]:9, tail:[1,3,4,8,9], curLen[7]:5
		arr[8]:7, tail:[1,3,4,7,9], curLen[8]:4  (8被替换成7,是以7为结尾的长度)
		ans = 5
		*/
        vector<int> res(tail_size); // 存放结果,虽然tail数组不是最终结果,但tail的大小为结果长度(最长上升子序列长度)
        // 逆序地从最大长度开始遍历
        int i,j;
        for(i=arr.size()-1, j=tail_size; i>=0; i--){
    
    
            if(curlen[i]==j)  // 以arr[i]为结尾的最长子序列长度为j说明arr[i]即为res的第j-1位
                // 因为相同长度(curlen相同)的情况下,越往后(i越大)的末尾元素越小(大的被小的替换了),所以从后往前取值可得到题目要求的结果
                res[--j] = arr[i];
            //j=5, curLen[i=7]:5, res[4]=arr[7], res: 0 0 0 0 9
			//j=4, curLen[i=6]:4, res[3]=arr[6], res: 0 0 0 8 9
			//j=3, curLen[i=5]:3, res[2]=arr[5], res: 0 0 4 8 9
			//j=2, curLen[i=3]:2, res[1]=arr[3], res: 0 3 4 8 9
			//j=1, curLen[i=1]:1, res[0]=arr[1], res: 1 3 4 8 9
        }
        return res;
    }
};

Acho que você gosta

Origin blog.csdn.net/zoey_peak/article/details/125680304
Recomendado
Clasificación