코딩 테스트 및 알고리즘/leetcode for google

leetcode : Sum of Subarray Ranges

띠리링구 2022. 9. 17. 20:04

Sum of Subarray Ranges - LeetCode

 

Sum of Subarray Ranges - LeetCode

Level up your coding skills and quickly land a job. This is the best place to expand your knowledge and get prepared for your next interview.

leetcode.com

 

문제를 해석해보면 주어진 array에서 임의의 i,j에 대해( 0 <= i <= j <= array.len-1) max(array[i:j]) - min(array[i:j])의 합을 구하라는 것이다. (i = j인 경우는 무조건 0이 나오니까 i < j 조건이 사실상 맞긴 하다.)

1, 2, ,3이 주어지면

(1,3) -> 2

(1,2) -> 1

(2,3) -> 1

합은 4

이런식이다.

100% naive하게 생각하면  O(N^3)의 시간복잡도가 나올 것이다. 각각의 원소에 대해 그 원소보다 오른쪽에 있는 원소들중 큰 것을 찾아 차를 구해줘야되기 때문이다.

array를 첫 번째 원소부터 마지막까지 스캔하면서 각 시점에 특정 조건의 '합'을 실시간으로 구할 수 있을까? range를 구하기 위해서는 최대값과 최소값을 알고있어야 한다.

monotonic stack(monotone stack | JusticeHui가 PS하는 블로그)을 이용한다면.. 인덱스 i를 볼때 array[0]부터 array[i]까지의 요소들 중 array[i]보다 큰값이나(내림차순) 작은값들을(오름차순) 빠르게 알 수 있다. 최대값, 최소값도 당연히 알 수 있다. 근데 어케해야될지 모르겠음 몇시간째 고민중인데.

 

Java | Well explained with examples | O(n) - LeetCode Discuss

 

Java | Well explained with examples | O(n) - LeetCode Discuss

Level up your coding skills and quickly land a job. This is the best place to expand your knowledge and get prepared for your next interview.

leetcode.com

와우.. 일단 기본아이디어는 이거.. 특정 원소가 min값인 걸 기준으로 잡는거랑 양쪽으로 구해서 곱하는거 생각은 했는데 (max-min) + (max-min) + ... 를 max끼리 묶고 min끼리 묶는건 생각도 못했네.

 

min stack과 max stack을 만들어 2스캔으로 풀었다.

pop하는 시점에 우리가값이 계산된다.

 

class Solution:
    def subArrayRanges(self, nums: List[int]) -> int:
        answer = 0
        
        stack = []
        i = 0
        for i in range(len(nums) + 1):
            v = nums[i] if i < len(nums) else -10**9-1
            while stack and nums[stack[-1]] >= v:
                ti = stack.pop()
                tv = nums[ti]
                
                k = stack[-1] if stack else -1 
                
                answer -= (i - ti) * (ti - k) * tv
            stack.append(i)
            
        stack = []
        for i in range(len(nums) + 1):
            v = nums[i] if i < len(nums) else 10**9+1
            while stack and nums[stack[-1]] <= v:
                ti = stack.pop()
                tv = nums[ti]
                
                k = stack[-1] if stack else -1
                
                answer += (i - ti) * (ti - k) * tv
            stack.append(i)
        
        return answer

 

공통부분을 함수로 묶어서 함수(max) - 함수(min)으로 구해도 된다.

long long subArrayRanges(vector<int>& n) {
    return sumSubarrayComp(n, less<int>()) - sumSubarrayComp(n, greater<int>());
}    
long long sumSubarrayComp(vector<int>& n, function<bool (int, int)> comp) {
    long long res = 0;
    vector<int> s;
    for (int i = 0; i <= n.size(); ++i) {
        while (!s.empty() && (i == n.size() || comp(n[s.back()], n[i]))) {
            int j = s.back(), k = s.size() < 2 ? -1 : s[s.size() - 2]; 
            res += (long long)(i - j) * (j - k) * n[j];
            s.pop_back();
        }
        s.push_back(i);
    }    
    return res;
}

 

이문제.. 재밌었다. 몇시간을 고민했네 진짜