class Solution:
    def numSubarraysWithSum(self, A: List[int], goal: int) -> int:
        """ tc O(N) sc O(1) (assuming each item in A is non-negtive )
        1. corner case if target < 0
        2. left, right ptr start with head. start with K, keep substract current item the right ptr pointing at
        3. keep reduce target K untill K < 0, start moving left ptr until K >=0
        4. each time we find a valid window [l,r], we add its length into res so we get the amount of subarrays within this subArray  
        
        """
        def atMost(K): # number of subarray at most goal
            if K < 0:
                return 0
            res = l = 0
            for r in range(len(A)):
                K -= A[r]
                while K < 0:
                    S += A[l]
                    l += 1
                res += r-l + 1
            return res 
        return atMost(goal) - atMost(goal-1)

Presum + HashMap

class Solution:
    def numSubarraysWithSum(self, A: List[int], goal: int) -> int:
        """ tc O(N) sc O(N)
        main idea:  count occurance of each psum, when psum - goal is seen before, update res with d[psum-goal]
                    it also apply to negtive 
                    note, we need to add corner case psum == goal +1  so d[0] = 1

        there are two cases: 
        (1)  psum[:j] == goal
        (2)  psum[i:j] == goal => psum[i:j] = psum[:j] - psum[:i-1] = goal   ============>   psum[:j] -goal = psum[:i-1]
            ====> we are looking for is there any `psum[:j] -goal` exist before, which will be # of (i,j) pairs
        """
        psum = res = 0
        d = defaultdict(int) #Counter({0:1})
        d[0] = 1 # this is importanc!!!!!!!!
        for a in A:
            psum += a
            #if psum >= goal:
            res += d[psum-goal]
            d[psum] += 1
            
        return res