Skip to content
Published on

FAANG Coding Interview Mastery: Arrays & Strings Core Patterns

Authors

FAANG Coding Interview Mastery: Arrays & Strings Core Patterns

Arrays and strings account for over 40% of FAANG (Facebook/Meta, Apple, Amazon, Netflix, Google) coding interview questions. This guide covers 6 essential patterns you must master, complete with working Python code and in-depth analysis.


1. Two Pointers Pattern

Concept and When to Use It

The Two Pointers pattern uses two indices to traverse a sorted array or string in linear time, eliminating the need for nested loops.

Use when:

  • Finding pairs in a sorted array that satisfy a condition
  • Comparing elements from both ends of an array
  • Removing duplicates or partitioning in-place

Core insight: Reduces O(n^2) brute force to O(n).


Problem 1: Two Sum II (LeetCode 167)

Given a sorted array, find two numbers that add up to the target.

def twoSum(numbers: list[int], target: int) -> list[int]:
    left, right = 0, len(numbers) - 1

    while left < right:
        current_sum = numbers[left] + numbers[right]
        if current_sum == target:
            return [left + 1, right + 1]  # 1-indexed
        elif current_sum < target:
            left += 1
        else:
            right -= 1

    return []

# Example
# Input: numbers = [2,7,11,15], target = 9
# Output: [1,2]

Time Complexity: O(n) — each pointer moves at most n steps total Space Complexity: O(1) — no extra space needed

FAANG Tip: For unsorted arrays, present a HashMap O(n) solution first, then explain why Two Pointers is more space-efficient on sorted input.


Problem 2: 3Sum (LeetCode 15)

Find all unique triplets in the array that sum to zero.

def threeSum(nums: list[int]) -> list[list[int]]:
    nums.sort()
    result = []

    for i in range(len(nums) - 2):
        # Skip duplicates for the first element
        if i > 0 and nums[i] == nums[i - 1]:
            continue

        left, right = i + 1, len(nums) - 1

        while left < right:
            total = nums[i] + nums[left] + nums[right]

            if total == 0:
                result.append([nums[i], nums[left], nums[right]])
                # Skip duplicates
                while left < right and nums[left] == nums[left + 1]:
                    left += 1
                while left < right and nums[right] == nums[right - 1]:
                    right -= 1
                left += 1
                right -= 1
            elif total < 0:
                left += 1
            else:
                right -= 1

    return result

# Example
# Input: nums = [-1, 0, 1, 2, -1, -4]
# Output: [[-1, -1, 2], [-1, 0, 1]]

Time Complexity: O(n^2) — sort O(n log n) plus double loop O(n^2) Space Complexity: O(1) to O(n) depending on sort algorithm

FAANG Tip: A frequent Meta interview question. Don't miss the duplicate-skipping logic — interviewers often probe for it.


Problem 3: Container With Most Water (LeetCode 11)

Given a height array, find the maximum area of water that can be contained.

def maxArea(height: list[int]) -> int:
    left, right = 0, len(height) - 1
    max_water = 0

    while left < right:
        width = right - left
        current_water = width * min(height[left], height[right])
        max_water = max(max_water, current_water)

        # Move the pointer at the shorter wall (seeking a taller one)
        if height[left] < height[right]:
            left += 1
        else:
            right -= 1

    return max_water

# Example
# Input: height = [1,8,6,2,5,4,8,3,7]
# Output: 49

Time Complexity: O(n) Space Complexity: O(1)

Why is this optimal? Moving the taller wall would only decrease the width while never increasing the limiting height — so it can never yield a larger area.


Problem 4: Trapping Rain Water (LeetCode 42)

Compute how much rainwater can be trapped between the bars.

def trap(height: list[int]) -> int:
    if not height:
        return 0

    left, right = 0, len(height) - 1
    left_max, right_max = height[left], height[right]
    water = 0

    while left < right:
        if left_max <= right_max:
            left += 1
            left_max = max(left_max, height[left])
            water += left_max - height[left]
        else:
            right -= 1
            right_max = max(right_max, height[right])
            water += right_max - height[right]

    return water

# Example
# Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]
# Output: 6

Time Complexity: O(n) Space Complexity: O(1) — no prefix max arrays needed

FAANG Tip: A staple at Google and Amazon. Know both the DP approach (O(n) space) and this Two Pointers approach, and be ready to explain why they are equivalent.


2. Sliding Window Pattern

Concept

The Sliding Window pattern finds optimal subarrays or substrings by maintaining a window that expands and contracts as it slides through the input.

Fixed Window: Window size is constant Variable Window: Window size adjusts based on a constraint


Problem 5: Longest Substring Without Repeating Characters (LeetCode 3)

def lengthOfLongestSubstring(s: str) -> int:
    char_set = set()
    left = 0
    max_length = 0

    for right in range(len(s)):
        while s[right] in char_set:
            char_set.remove(s[left])
            left += 1

        char_set.add(s[right])
        max_length = max(max_length, right - left + 1)

    return max_length

# Optimized version using a HashMap
def lengthOfLongestSubstringOptimized(s: str) -> int:
    char_index = {}
    left = 0
    max_length = 0

    for right, char in enumerate(s):
        if char in char_index and char_index[char] >= left:
            left = char_index[char] + 1

        char_index[char] = right
        max_length = max(max_length, right - left + 1)

    return max_length

# Example
# Input: "abcabcbb"
# Output: 3 ("abc")

Time Complexity: O(n) Space Complexity: O(min(n, charset_size))


Problem 6: Minimum Window Substring (LeetCode 76)

Find the smallest window in s that contains all characters of t.

from collections import Counter

def minWindow(s: str, t: str) -> str:
    if not t or not s:
        return ""

    need = Counter(t)
    missing = len(t)  # Characters still needed

    best_start = 0
    best_end = float('inf')
    left = 0

    for right, char in enumerate(s):
        if need[char] > 0:
            missing -= 1
        need[char] -= 1

        if missing == 0:
            # Shrink from the left
            while need[s[left]] < 0:
                need[s[left]] += 1
                left += 1

            if right - left < best_end - best_start:
                best_start = left
                best_end = right

            # Slide the window forward
            need[s[left]] += 1
            missing += 1
            left += 1

    if best_end == float('inf'):
        return ""
    return s[best_start:best_end + 1]

# Example
# Input: s = "ADOBECODEBANC", t = "ABC"
# Output: "BANC"

Time Complexity: O(|s| + |t|) Space Complexity: O(|s| + |t|)


Problem 7: Sliding Window Maximum (LeetCode 239)

Return the maximum of each sliding window of size k.

from collections import deque

def maxSlidingWindow(nums: list[int], k: int) -> list[int]:
    dq = deque()  # Stores indices in decreasing order of values
    result = []

    for i, num in enumerate(nums):
        # Remove indices outside the window
        if dq and dq[0] < i - k + 1:
            dq.popleft()

        # Remove indices with smaller values (they can never be the max)
        while dq and nums[dq[-1]] < num:
            dq.pop()

        dq.append(i)

        # Start recording once the first window is full
        if i >= k - 1:
            result.append(nums[dq[0]])

    return result

# Example
# Input: nums = [1,3,-1,-3,5,3,6,7], k = 3
# Output: [3,3,5,5,6,7]

Time Complexity: O(n) — each element is added/removed from deque at most once Space Complexity: O(k)


Problem 8: Fruit Into Baskets (LeetCode 904)

Find the maximum number of fruits you can collect if you can only carry at most 2 types.

from collections import defaultdict

def totalFruit(fruits: list[int]) -> int:
    basket = defaultdict(int)
    left = 0
    max_fruits = 0

    for right, fruit in enumerate(fruits):
        basket[fruit] += 1

        # Shrink window when we have more than 2 fruit types
        while len(basket) > 2:
            basket[fruits[left]] -= 1
            if basket[fruits[left]] == 0:
                del basket[fruits[left]]
            left += 1

        max_fruits = max(max_fruits, right - left + 1)

    return max_fruits

# Example
# Input: fruits = [1,2,1,2,3]
# Output: 4 ([1,2,1,2])

Time Complexity: O(n) Space Complexity: O(1) — at most 3 fruit types stored at once


3. Binary Search Pattern

Concept

Binary Search finds elements in a sorted array in O(log n) time. FAANG interviews frequently test non-standard variants.

Standard Binary Search Template:

def binarySearch(nums: list[int], target: int) -> int:
    left, right = 0, len(nums) - 1

    while left <= right:
        mid = left + (right - left) // 2  # Avoids integer overflow

        if nums[mid] == target:
            return mid
        elif nums[mid] < target:
            left = mid + 1
        else:
            right = mid - 1

    return -1

Problem 9: Search in Rotated Sorted Array (LeetCode 33)

def search(nums: list[int], target: int) -> int:
    left, right = 0, len(nums) - 1

    while left <= right:
        mid = left + (right - left) // 2

        if nums[mid] == target:
            return mid

        # Left half is sorted
        if nums[left] <= nums[mid]:
            if nums[left] <= target < nums[mid]:
                right = mid - 1
            else:
                left = mid + 1
        # Right half is sorted
        else:
            if nums[mid] < target <= nums[right]:
                left = mid + 1
            else:
                right = mid - 1

    return -1

# Example
# Input: nums = [4,5,6,7,0,1,2], target = 0
# Output: 4

Time Complexity: O(log n) Space Complexity: O(1)


Problem 10: Find Minimum in Rotated Sorted Array (LeetCode 153)

def findMin(nums: list[int]) -> int:
    left, right = 0, len(nums) - 1

    while left < right:
        mid = left + (right - left) // 2

        if nums[mid] > nums[right]:
            left = mid + 1  # Min is in the right half
        else:
            right = mid     # Mid could be the minimum

    return nums[left]

# Example
# Input: nums = [3,4,5,1,2]
# Output: 1

Time Complexity: O(log n) Space Complexity: O(1)


Problem 11: Median of Two Sorted Arrays (LeetCode 4) — Hard

def findMedianSortedArrays(nums1: list[int], nums2: list[int]) -> float:
    # Ensure nums1 is the smaller array
    if len(nums1) > len(nums2):
        nums1, nums2 = nums2, nums1

    m, n = len(nums1), len(nums2)
    half_len = (m + n + 1) // 2

    left, right = 0, m

    while left <= right:
        i = (left + right) // 2  # Partition index in nums1
        j = half_len - i          # Partition index in nums2

        if i < m and nums1[i] < nums2[j - 1]:
            left = i + 1
        elif i > 0 and nums1[i - 1] > nums2[j]:
            right = i - 1
        else:
            # Correct partition found
            if i == 0:
                max_left = nums2[j - 1]
            elif j == 0:
                max_left = nums1[i - 1]
            else:
                max_left = max(nums1[i - 1], nums2[j - 1])

            if (m + n) % 2 == 1:
                return float(max_left)

            if i == m:
                min_right = nums2[j]
            elif j == n:
                min_right = nums1[i]
            else:
                min_right = min(nums1[i], nums2[j])

            return (max_left + min_right) / 2.0

# Example
# Input: nums1 = [1,3], nums2 = [2]
# Output: 2.0

Time Complexity: O(log(min(m, n))) Space Complexity: O(1)


Problem 12: Search a 2D Matrix (LeetCode 74)

def searchMatrix(matrix: list[list[int]], target: int) -> bool:
    if not matrix or not matrix[0]:
        return False

    rows, cols = len(matrix), len(matrix[0])
    left, right = 0, rows * cols - 1

    while left <= right:
        mid = left + (right - left) // 2
        # Map 1D index to 2D coordinates
        mid_val = matrix[mid // cols][mid % cols]

        if mid_val == target:
            return True
        elif mid_val < target:
            left = mid + 1
        else:
            right = mid - 1

    return False

# Example
# Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
# Output: True

Time Complexity: O(log(m*n)) Space Complexity: O(1)


4. Prefix Sum & Hash Map Pattern

Concept

Prefix Sum precomputes cumulative sums so that range sum queries run in O(1). Combined with a HashMap, it finds subarrays satisfying specific conditions efficiently.


Problem 13: Subarray Sum Equals K (LeetCode 560)

Count the number of contiguous subarrays whose sum equals k.

from collections import defaultdict

def subarraySum(nums: list[int], k: int) -> int:
    prefix_sum_count = defaultdict(int)
    prefix_sum_count[0] = 1  # Empty prefix has sum 0

    current_sum = 0
    count = 0

    for num in nums:
        current_sum += num
        # If (current_sum - k) was seen before, those subarrays sum to k
        count += prefix_sum_count[current_sum - k]
        prefix_sum_count[current_sum] += 1

    return count

# Example
# Input: nums = [1,1,1], k = 2
# Output: 2

Time Complexity: O(n) Space Complexity: O(n)


Problem 14: Longest Consecutive Sequence (LeetCode 128)

Find the length of the longest consecutive sequence in O(n) time.

def longestConsecutive(nums: list[int]) -> int:
    num_set = set(nums)
    max_length = 0

    for num in num_set:
        # Only start a sequence from its lowest element
        if num - 1 not in num_set:
            current = num
            length = 1

            while current + 1 in num_set:
                current += 1
                length += 1

            max_length = max(max_length, length)

    return max_length

# Example
# Input: nums = [100,4,200,1,3,2]
# Output: 4 ([1,2,3,4])

Time Complexity: O(n) — each number is visited at most twice Space Complexity: O(n)


Problem 15: Group Anagrams (LeetCode 49)

Group strings that are anagrams of each other.

from collections import defaultdict

def groupAnagrams(strs: list[str]) -> list[list[str]]:
    anagram_map = defaultdict(list)

    for s in strs:
        key = tuple(sorted(s))
        anagram_map[key].append(s)

    return list(anagram_map.values())

# Optimized version using character count as key
def groupAnagramsOptimized(strs: list[str]) -> list[list[str]]:
    anagram_map = defaultdict(list)

    for s in strs:
        count = [0] * 26
        for c in s:
            count[ord(c) - ord('a')] += 1
        anagram_map[tuple(count)].append(s)

    return list(anagram_map.values())

# Example
# Input: strs = ["eat","tea","tan","ate","nat","bat"]
# Output: [["bat"],["nat","tan"],["ate","eat","tea"]]

Time Complexity: O(n _ k) — n strings, k is max string length Space Complexity: O(n _ k)


Problem 16: Top K Frequent Elements (LeetCode 347)

from collections import Counter
import heapq

def topKFrequent(nums: list[int], k: int) -> list[int]:
    # Approach 1: Heap — O(n log k)
    count = Counter(nums)
    return heapq.nlargest(k, count.keys(), key=count.get)

def topKFrequentBucketSort(nums: list[int], k: int) -> list[int]:
    # Approach 2: Bucket Sort — O(n)
    count = Counter(nums)

    # Index = frequency, value = list of numbers with that frequency
    bucket = [[] for _ in range(len(nums) + 1)]
    for num, freq in count.items():
        bucket[freq].append(num)

    result = []
    for freq in range(len(bucket) - 1, 0, -1):
        for num in bucket[freq]:
            result.append(num)
            if len(result) == k:
                return result

    return result

# Example
# Input: nums = [1,1,1,2,2,3], k = 2
# Output: [1,2]

Time Complexity: O(n) with Bucket Sort Space Complexity: O(n)


Google

Google interviews are Array and String manipulation heavy.

  • Frequent: Two Sum variants, string parsing, 2D array traversal
  • Key skills: Edge case handling, code clarity and readability
  • Example problems: Longest Substring with At Most K Distinct Characters, Minimum Number of Arrows to Burst Balloons

Meta (Facebook)

Meta favors Sliding Window and Two Pointer patterns.

  • Frequent: 3Sum, Valid Palindrome II, Move Zeroes
  • Key skills: Explaining optimization rationale, concise code
  • Example problems: Subarray Product Less Than K, Permutation in String

Amazon

Amazon leans toward Hash Table and Sorting based problems.

  • Frequent: Two Sum, Group Anagrams, Top K Frequent
  • Key skills: Practical solutions, tying to Leadership Principles
  • Example problems: K Closest Points to Origin, Sort Colors

Microsoft

Microsoft asks Binary Search variants and array manipulation.

  • Frequent: Find Peak Element, Search a 2D Matrix
  • Key skills: Rigorous edge case coverage, bug-free code
  • Example problems: Spiral Matrix, Rotate Image

6. Interview Strategy

Problem-Solving Framework (45 Minutes)

Step 1 — Clarify (5 minutes)

Always ask the interviewer:

  • Input range and types (can values be negative? duplicates allowed?)
  • Output format (indices? values? count?)
  • Edge cases (empty array, single element, all same value)
  • Optimization target (time vs. space)

Step 2 — Brute Force (5 minutes)

State a naive O(n^2) or O(n^3) solution. This establishes a baseline.

Step 3 — Optimize (10 minutes)

Identify the pattern: Two Pointers? Sliding Window? Hash Map? Discuss trade-offs with the interviewer before coding.

Step 4 — Code (15 minutes)

Write clean code with meaningful variable names:

def solve(nums, target):
    # Initialize two pointers
    left, right = 0, len(nums) - 1

    while left < right:
        # ... logic
        pass

Step 5 — Test (10 minutes)

Walk through your own test cases:

  • Normal case
  • Edge cases (empty array, length 1)
  • Worst case (all duplicates, reverse sorted)

Edge Case Checklist

Always verify these for array/string problems:

  • Empty input ([], "")
  • Single element ([5], "a")
  • All identical values ([3,3,3,3])
  • Already sorted input
  • Reverse sorted input
  • Integer overflow potential
  • Negative numbers

Time Allocation Table

PhaseTimeKey Focus
Clarify5 minAsk questions, nail requirements
Brute Force5 minEstablish baseline solution
Optimize10 minIdentify pattern, improve complexity
Coding15 minClean, bug-free implementation
Testing10 minTrace through, catch edge cases

7. Practice Quiz

Quiz 1: Remove Duplicates from Sorted Array with Two Pointers

Given sorted array nums = [0,0,1,1,1,2,2,3,3,4], remove duplicates in-place using O(1) extra space and return the count of unique elements.

Answer: 5 (array becomes [0,1,2,3,4,...])

Explanation: Use slow and fast pointers. The slow pointer tracks the next write position. When nums[fast] != nums[slow], increment slow and overwrite.

def removeDuplicates(nums):
    if not nums:
        return 0
    slow = 0
    for fast in range(1, len(nums)):
        if nums[fast] != nums[slow]:
            slow += 1
            nums[slow] = nums[fast]
    return slow + 1
Quiz 2: Max Consecutive Ones with One Flip

Binary array nums = [1,1,0,0,1,1,1,0,1]. You may flip at most k=1 zeros to ones. Find the maximum length of a contiguous subarray of ones.

Answer: 6

Explanation: Variable Sliding Window. Track the count of zeros in the window; shrink from the left when zeros exceed k.

def longestOnes(nums, k):
    left = 0
    zeros = 0
    max_len = 0
    for right in range(len(nums)):
        if nums[right] == 0:
            zeros += 1
        while zeros > k:
            if nums[left] == 0:
                zeros -= 1
            left += 1
        max_len = max(max_len, right - left + 1)
    return max_len
Quiz 3: Binary Search for First and Last Position

Sorted array nums = [5,7,7,8,8,10], target = 8. Find the first and last index.

Answer: [3, 4]

Explanation: Run Binary Search twice — once biased left (continue searching left after finding target), once biased right.

def searchRange(nums, target):
    def findLeft(nums, target):
        left, right, idx = 0, len(nums) - 1, -1
        while left <= right:
            mid = (left + right) // 2
            if nums[mid] == target:
                idx = mid
                right = mid - 1
            elif nums[mid] < target:
                left = mid + 1
            else:
                right = mid - 1
        return idx

    def findRight(nums, target):
        left, right, idx = 0, len(nums) - 1, -1
        while left <= right:
            mid = (left + right) // 2
            if nums[mid] == target:
                idx = mid
                left = mid + 1
            elif nums[mid] < target:
                left = mid + 1
            else:
                right = mid - 1
        return idx

    return [findLeft(nums, target), findRight(nums, target)]
Quiz 4: Count Subarrays with Zero Sum using Prefix Sum

Array [1, -1, 1, -1, 1] — how many contiguous subarrays have sum equal to 0?

Answer: 4

Explanation: Use Prefix Sum + HashMap. Every time the same prefix sum appears again, all subarrays between those two positions have sum 0. Count with count += prefix_sum_count[current_sum - k].

from collections import defaultdict
def subarraySum(nums, k=0):
    prefix_sum_count = defaultdict(int)
    prefix_sum_count[0] = 1
    current_sum = 0
    count = 0
    for num in nums:
        current_sum += num
        count += prefix_sum_count[current_sum - k]
        prefix_sum_count[current_sum] += 1
    return count
# Result: 4
Quiz 5: Product of Array Except Self

Array nums = [1,2,3,4]. Return an array where each element is the product of all other elements. No division allowed. O(n) time, O(1) extra space.

Answer: [24, 12, 8, 6]

Explanation: Two passes — first accumulate left products into the result array, then multiply by right products in a second pass from the right.

def productExceptSelf(nums):
    n = len(nums)
    result = [1] * n

    # Pass 1: left products
    prefix = 1
    for i in range(n):
        result[i] = prefix
        prefix *= nums[i]

    # Pass 2: multiply by right products
    suffix = 1
    for i in range(n - 1, -1, -1):
        result[i] *= suffix
        suffix *= nums[i]

    return result

Conclusion

Arrays and strings are the bedrock of FAANG coding interviews. Mastering these 6 patterns lets you recognize most problems instantly and approach them with an optimal strategy.

Next steps:

  1. Solve 20 problems per pattern on LeetCode
  2. Practice under a 45-minute timer
  3. Code review: compare your solution with top-voted answers
  4. Flip the script: try creating your own problem variants

The next guide in this series covers Trees and Graphs patterns.