Many array and string problems can be solved with two indices moving through the data, instead of nested loops. Replacing an O(n²) double loop with two pointers that each move O(n) times is one of the most common speed-ups you'll use.
Converging pointers start at opposite ends and move toward each other. Great for palindromes, reversing, and "find a pair in a sorted array":
left, right = 0, length(s) - 1
while left < right:
// compare or use s[left] and s[right]
left += 1
right -= 1
Fast / slow (read / write) pointers both start at the left, moving at different rates. Great for filtering or compacting an array in place ā one pointer reads, the other writes:
write = 0
for read in 0..length(nums)-1:
if keep(nums[read]):
nums[write] = nums[read]
write += 1
// nums[0..write-1] now holds the kept elements
Each pointer advances at most n times, so the whole scan is O(n) with O(1) extra space (for in-place variants). The skill is choosing which flavour fits and defining the rule for advancing each pointer. The problems in this section drill both: comparing ends (palindrome, subsequence) and read/write compaction (five sort, compress/uncompress).