Implement a function that powers a debounced search feature over a list of strings. Given a sequence of timestamped query updates and a dictionary of searchable items, return the search results emitted after debouncing each burst of input. Matching must be case-insensitive and based on substring containment. Repeated queries should reuse cached results instead of recomputing them.
def debounced_search(items, events, debounce_ms):