LeetCode: 139. Word Break
Summary
Given a string s and a dictionary wordDict, determine whether s can be segmented into a space-separated sequence of one or more dictionary words.
Each word can be reused multiple times.
Approach
Use a trie and track multiple active matching states while scanning s.
We cannot follow only one path because words can share prefixes (for example, "app", "apple", and "application").
So at each character, we keep all possible trie nodes that represent valid partial matches.
At the end of the scan, if any active state is at a terminal trie node, the answer is true.
Algorithm
Initialization:
trie_root= Build a trie fromwordDict.states = [trie_root](active trie nodes after consuming the current prefix ofs).
Main Loop:
For each character ch in s:
- Initialize:
visitedas a set of node IDs to avoid duplicate nodes in this step.new_statesas an empty array.
- For each node in
states:- If the node has child
ch, move to that child and add it tonew_statesif not visited. - If the current node is terminal (end of a word), we can start a new word:
- If
trie_roothas childch, addtrie_root[ch]tonew_statesif not visited.
- If
- If the node has child
- Replace
stateswithnew_states. - Also start from the beginning of
s:- If
trie_roothas childch, include it innew_statesfor the first character (or whenever no active path exists yet).
- If
Return true if any node in states is terminal after the final character; otherwise return false.
Example:
s = "leetcode", wordDict = ["leet", "code"]
trie = { l: { e: ... }, c: { o: ... } }
states = []
states = [root['l']]states = [root['l']['e']]- ...
states = [root['l']['e']['e']['t']](terminal:"leet")- Start new word from root with
'c':states = [root['c']] - ...
states = [root['c']['o']['d']['e']](terminal:"code")
Complexity
Time complexity: O(NK + SW)
N: number of words inwordDictK: average word lengthS: length ofsW: maximum word length inwordDict
The NK term is for building the trie.
The SW term is for scanning s while tracking up to W active states in the worst case.
Worst case for len(states):
- example: wordDict = ['a', 'aa', 'aaa', 'aaaa'], s = 'aaaaa'
- then states evolve like:
- 1st loop: states = [root['a']]
- 2nd loop: states = [root['a'], root['a']['a']]
- 3rd loop: states = [root['a'], root['a']['a'], root['a']['a']['a']]
- 4th loop: states = [root['a'], root['a']['a'], root['a']['a']['a'], root['a']['a']['a']['a']]
- 5th loop: same as 4th (no further growth)
Space complexity: O(NK) for storing the trie (plus O(W) for active states).