Content Safety Filter
WhatnotSoftware Engineer · Technical ScreenLast updated
Tests string tokenization, interval tracking, and Trie-based multi-word pattern matching to redact prohibited phrases from live-stream messages.
Content Safety Filter
In a live commerce platform, buyers and sellers interact in real-time chat during auctions. To prevent fraud and abuse, incoming chat messages must be filtered to mask prohibited terms and phrases before being broadcasted.
Write a function filter_content(message: str, banned_phrases: list[str]) -> str that detects all occurrences of prohibited phrases in message and replaces every alphanumeric character in the matched words with an asterisk (*). Non-alphanumeric characters (punctuation, symbols, whitespace) must be preserved in their original form and positions.
Rules
- Case-Insensitive: Matching between
messageandbanned_phrasesis case-insensitive. - Word Boundaries: Matches must align with word boundaries. A word is defined as a maximal contiguous sequence of alphanumeric characters (
[a-zA-Z0-9]). Punctuation and whitespace act as delimiters. - Multi-Word Phrases: A banned phrase may contain multiple words separated by single spaces. In the message, words in a multi-word phrase can be separated by any sequence of non-alphanumeric characters (whitespace or punctuation).
- Preservation: Punctuation and spacing must remain intact; only the alphanumeric characters of matched words are masked.
- Overlaps: If matched phrases overlap, all characters belonging to any matched phrase must be masked.
Function Signature
def filter_content(message: str, banned_phrases: list[str]) -> str:
pass
Constraints
1 <= len(message) <= 10^51 <= len(banned_phrases) <= 5 * 10^4- Total words across all banned phrases
<= 10^5 banned_phrasescontains only alphanumeric words separated by single spaces.- Target time complexity:
O(N + M)whereNis the length ofmessageandMis the total character length ofbanned_phrases.
Examples
Example 1:
message = "Check out this fake giveaway! Don't buy."
banned_phrases = ["fake giveaway", "buy"]
output = filter_content(message, banned_phrases)
# output: "Check out this **** ********! Don't ***."
Explanation: "fake giveaway" matches the two words "fake" and "giveaway". "buy" matches "buy". All alphanumeric characters are replaced with *, preserving whitespace and punctuation.
Example 2:
message = "Scam-alert: Free coins at url!"
banned_phrases = ["scam alert", "free coins"]
output = filter_content(message, banned_phrases)
# output: "****-*****: **** ***** at url!"
Explanation: In "Scam-alert", the hyphen acts as a non-alphanumeric separator, matching the phrase "scam alert".
Edge Cases
- Partial word matches must not trigger a replacement (e.g.,
"buy"should not match inside"buyer"). - Consecutive punctuation or whitespace between words should not disrupt multi-word phrase matching.
Hints
Hint 1
Tokenize the message by extracting each alphanumeric word along with its start and end character indices in the original string.
Hint 2
Build a Trie where each node represents an entire word (case-normalized) rather than a single character, marking nodes where complete banned phrases end.
Hint 3
Iterate through the message tokens, traversing the word-Trie to find all matching phrase spans, and mask the recorded character intervals in the original string.
Follow-up questions
- How would you adapt your filter to handle deliberate obfuscations such as leetspeak (e.g., replacing 'e' with '3') or repeated characters (e.g., 'scaaam')?
- If the list of banned phrases is dynamically updated by a trust & safety team multiple times per minute, how would you design the cache and synchronization mechanism across chat servers?
- How would you optimize the search if banned phrases contained arbitrary regular expressions or wildcards rather than static word sequences?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.