# Prefix Sum Pattern

## Overview
Prefix sum is a powerful technique for efficiently answering range sum queries and solving various array problems. It involves precomputing cumulative sums to allow O(1) range queries.

## Core Concepts

### Prefix Sum Definition
The prefix sum array `prefix` is defined such that:
```
prefix[i] = arr[0] + arr[1] + ... + arr[i]
```

Given a range query from index `i` to `j`:
```
sum(i, j) = prefix[j] - prefix[i-1]
```

### Basic Implementation
```typescript
function buildPrefixSum(arr: number[]): number[] {
    const prefix = new Array(arr.length);
    prefix[0] = arr[0];
    
    for (let i = 1; i < arr.length; i++) {
        prefix[i] = prefix[i-1] + arr[i];
    }
    
    return prefix;
}

function rangeSum(prefix: number[], left: number, right: number): number {
    if (left === 0) {
        return prefix[right];
    }
    return prefix[right] - prefix[left-1];
}
```

## Variations

### 1. Prefix Product
```typescript
function buildPrefixProduct(arr: number[]): number[] {
    const prefix = new Array(arr.length);
    prefix[0] = arr[0];
    
    for (let i = 1; i < arr.length; i++) {
        prefix[i] = prefix[i-1] * arr[i];
    }
    
    return prefix;
}

function rangeProduct(prefix: number[], left: number, right: number): number {
    if (left === 0) {
        return prefix[right];
    }
    return prefix[right] / prefix[left-1];
}
```

### 2. 2D Prefix Sum
```typescript
function build2DPrefixSum(matrix: number[][]): number[][] {
    const rows = matrix.length;
    const cols = matrix[0].length;
    const prefix = Array.from({ length: rows + 1 }, () => Array(cols + 1).fill(0));
    
    for (let i = 1; i <= rows; i++) {
        for (let j = 1; j <= cols; j++) {
            prefix[i][j] = matrix[i-1][j-1] + 
                          prefix[i-1][j] + 
                          prefix[i][j-1] - 
                          prefix[i-1][j-1];
        }
    }
    
    return prefix;
}

function range2DSum(prefix: number[][], row1: number, col1: number, row2: number, col2: number): number {
    return prefix[row2+1][col2+1] - 
           prefix[row1][col2+1] - 
           prefix[row2+1][col1] + 
           prefix[row1][col1];
}
```

### 3. Prefix XOR
```typescript
function buildPrefixXOR(arr: number[]): number[] {
    const prefix = new Array(arr.length);
    prefix[0] = arr[0];
    
    for (let i = 1; i < arr.length; i++) {
        prefix[i] = prefix[i-1] ^ arr[i];
    }
    
    return prefix;
}

function rangeXOR(prefix: number[], left: number, right: number): number {
    if (left === 0) {
        return prefix[right];
    }
    return prefix[right] ^ prefix[left-1];
}
```

## Applications

### 1. Subarray Sum Equals K
```typescript
function subarraySum(nums: number[], k: number): number {
    const prefixSum = { 0: 1 };
    let currentSum = 0;
    let count = 0;
    
    for (const num of nums) {
        currentSum += num;
        
        // Check if there's a prefix that sums to currentSum - k
        if (prefixSum[currentSum - k]) {
            count += prefixSum[currentSum - k];
        }
        
        // Store current prefix sum
        prefixSum[currentSum] = (prefixSum[currentSum] || 0) + 1;
    }
    
    return count;
}
```

### 2. Find Maximum Subarray Sum
```typescript
function maxSubArray(nums: number[]): number {
    let maxSum = nums[0];
    let currentSum = nums[0];
    
    for (let i = 1; i < nums.length; i++) {
        currentSum = Math.max(nums[i], currentSum + nums[i]);
        maxSum = Math.max(maxSum, currentSum);
    }
    
    return maxSum;
}
```

### 3. Product of Array Except Self
```typescript
function productExceptSelf(nums: number[]): number[] {
    const n = nums.length;
    const left = new Array(n);
    const right = new Array(n);
    const result = new Array(n);
    
    // Left prefix products
    left[0] = 1;
    for (let i = 1; i < n; i++) {
        left[i] = left[i-1] * nums[i-1];
    }
    
    // Right prefix products
    right[n-1] = 1;
    for (let i = n-2; i >= 0; i--) {
        right[i] = right[i+1] * nums[i+1];
    }
    
    // Result is product of left and right
    for (let i = 0; i < n; i++) {
        result[i] = left[i] * right[i];
    }
    
    return result;
}
```

## Time Complexity Analysis

| Operation | Time Complexity | Space Complexity | Notes |
|-----------|----------------|----------------|---------|
| Build Prefix Sum | O(n) | O(n) | One pass through array |
| Range Query | O(1) | O(1) | Constant time lookup |
| 2D Prefix Sum | O(mn) | O(mn) | For m×n matrix |
| 2D Range Query | O(1) | O(1) | Rectangle sum query |

## Best Practices

### 1. Handling Edge Cases
```typescript
// Handle empty array
if (arr.length === 0) return [];

// Handle single element
if (arr.length === 1) return [arr[0]];

// Handle negative numbers
// Prefix sum works with negatives too
```

### 2. Space Optimization
```typescript
// In-place prefix sum (if input can be modified)
for (let i = 1; i < arr.length; i++) {
    arr[i] += arr[i-1];
}
```

### 3. Multiple Prefix Arrays
```typescript
// For different operations, build separate prefix arrays
const sumPrefix = buildPrefixSum(arr);
const xorPrefix = buildPrefixXOR(arr);
```

## Common Mistakes

### 1. Index Errors
```typescript
// Wrong: off-by-one error
prefix[i] = prefix[i] + arr[i]; // Should use i-1

// Correct: proper indexing
prefix[i] = prefix[i-1] + arr[i];
```

### 2. Range Query Errors
```typescript
// Wrong: incorrect range calculation
sum = prefix[right] - prefix[left]; // Misses left element

// Correct: proper range calculation
sum = prefix[right] - prefix[left-1];
```

### 3. Integer Overflow
```typescript
// Problem: Large numbers can cause overflow
// Solution: Use BigInt or modulo operation
const prefix = new Array(n).fill(BigInt(0));
```

### 4. Not Handling Negative Numbers
```typescript
// Prefix sums work with negatives automatically
// No special handling needed
```

## Practice Problems

### Easy
- [ ] Range sum query
- [ ] Prefix sum array construction
- [ ] Sum of subarray
- [ ] Product of array except self
- [ ] Find pivot index

### Medium
- [ ] Subarray sum equals K
- [ Maximum subarray (Kadane's algorithm)
- [ ] Contiguous array
- [ ] Find the shortest subarray
- [ ] Minimum size subarray sum

### Hard
- [ ] Maximum sum circular subarray
- [ ] Subarray product less than K
- [ ] Find longest subarray with equal 0s and 1s
- [ ] 2D range sum queries
- [ ] Range sum query 2D (immutable)

## Real-world Applications

1. **Data Analysis**: Time series analysis, moving averages
2. **Image Processing**: Integral images for fast feature computation
3. **Database Queries**: Range queries on indexed data
4. **Game Development**: Collision detection, spatial queries

## Advanced Techniques

### 1. Dynamic Programming with Prefix Sums
```typescript
function countSubarrays(arr: number[], target: number): number {
    const prefixMap = new Map<number, number>();
    prefixMap.set(0, 1);
    let currentSum = 0;
    let count = 0;
    
    for (let i = 0; i < arr.length; i++) {
        currentSum += arr[i];
        
        // Find how many times currentSum - target has occurred
        if (prefixMap.has(currentSum - target)) {
            count += prefixMap.get(currentSum - target);
        }
        
        // Update prefix map
        prefixMap.set(currentSum, (prefixMap.get(currentSum) || 0) + 1);
    }
    
    return count;
}
```

### 2. Sliding Window with Prefix Sums
```typescript
function findMaxAverage(nums: number[], k: number): number {
    // Build prefix sum
    const prefix = new Array(nums.length);
    prefix[0] = nums[0];
    for (let i = 1; i < nums.length; i++) {
        prefix[i] = prefix[i-1] + nums[i];
    }
    
    let maxSum = prefix[k-1];
    for (let i = k; i < nums.length; i++) {
        const currentSum = prefix[i] - prefix[i-k];
        maxSum = Math.max(maxSum, currentSum);
    }
    
    return maxSum / k;
}
```

## Tips for Mastery

### 1. Understand the Mathematics
- Prefix sum is cumulative addition
- Range queries use the inclusion-exclusion principle
- The technique works for any associative operation (sum, product, min, max)

### 2. Practice Different Operations
- Sum, product, XOR, min, max
- 1D and 2D applications
- Dynamic programming combinations

### 3. Optimize Space
- In-place modifications when possible
- Single pass algorithms
- Space-efficient data structures

### 4. Combine with Other Patterns
- Sliding window
- Binary search
- Hash tables for frequency counting

## Next Steps
1. Master basic prefix sum construction and queries
2. Practice 1D and 2D applications
3. Learn to combine with dynamic programming
4. Apply to real-world problems

---

**Key Takeaway**: Prefix sum is a fundamental technique for range queries and subarray problems. It transforms O(n) range queries into O(1) operations with O(n) preprocessing time. Always consider prefix sums when dealing with range-based array problems.