Word Break Problem in DSA C# - Time & Space Complexity
We want to understand how the time needed to solve the Word Break Problem changes as the input grows.
Specifically, how does checking if a string can be split into dictionary words scale with string length?
Analyze the time complexity of the following code snippet.
public bool WordBreak(string s, IList<string> wordDict) {
var wordSet = new HashSet<string>(wordDict);
var dp = new bool[s.Length + 1];
dp[0] = true;
for (int i = 1; i <= s.Length; i++) {
for (int j = 0; j < i; j++) {
if (dp[j] && wordSet.Contains(s.Substring(j, i - j))) {
dp[i] = true;
break;
}
}
}
return dp[s.Length];
}
This code checks if the string can be split into words from the dictionary using dynamic programming.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Nested loops where for each position in the string, we check all previous positions.
- How many times: Outer loop runs n times (string length), inner loop runs up to n times, so roughly n x n = n² checks.
- Each check includes a substring extraction and dictionary lookup.
As the string length grows, the number of checks grows roughly with the square of the length.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 100 checks |
| 100 | About 10,000 checks |
| 1000 | About 1,000,000 checks |
Pattern observation: Doubling the string length roughly quadruples the work.
Time Complexity: O(n³)
This means the time grows roughly with the cube of the string length due to substring extraction and dictionary lookup.
[X] Wrong: "The algorithm only checks each character once, so it must be O(n)."
[OK] Correct: Because for each position, it checks all previous positions, making it a nested loop, so the work grows much faster than just n.
Understanding this time complexity helps you explain how dynamic programming improves over naive recursion and shows your grasp of nested loops and string operations.
"What if we used a Trie data structure for the dictionary instead of a HashSet? How would the time complexity change?"
Practice
s and a dictionary of words wordDict?Solution
Step 1: Understand the problem statement
The Word Break Problem asks if the stringscan be split into words all found in the dictionarywordDict.Step 2: Identify the correct description
Whetherscan be segmented into a space-separated sequence of one or more dictionary words correctly describes this segmentation check, while others describe unrelated tasks.Final Answer:
Whether s can be segmented into a space-separated sequence of one or more dictionary words -> Option BQuick Check:
Word Break Problem = segmentation check [OK]
- Confusing Word Break with palindrome checking
- Thinking it counts characters instead of segmenting
- Assuming it finds longest word instead of segmentation
Solution
Step 1: Check parameter types for Word Break
The input stringsshould be a string, and the dictionarywordDictshould be a list of strings.Step 2: Verify return type and parameters
public bool WordBreak(string s, IList<string> wordDict) matches the expected signature: returns bool, takes string and IList<string>. Others have wrong types or return types.Final Answer:
public bool WordBreak(string s, IList<string> wordDict) -> Option CQuick Check:
Correct method signature = public bool WordBreak(string s, IList<string> wordDict) [OK]
- Using wrong return type like int or string
- Passing dictionary as string instead of list
- Using wrong parameter types like int for string
var s = "leetcode";
var wordDict = new List<string> { "leet", "code" };
var result = WordBreak(s, wordDict);
Console.WriteLine(result);
bool WordBreak(string s, IList<string> wordDict) {
var dp = new bool[s.Length + 1];
dp[0] = true;
for (int i = 1; i <= s.Length; i++) {
for (int j = 0; j < i; j++) {
if (dp[j] && wordDict.Contains(s.Substring(j, i - j))) {
dp[i] = true;
break;
}
}
}
return dp[s.Length];
}Solution
Step 1: Understand the dynamic programming logic
The code uses a boolean arraydpwheredp[i]is true if substrings[0..i-1]can be segmented into dictionary words.Step 2: Trace the substring checks for "leetcode"
"leet" and "code" are inwordDict. The code finds "leet" at start and then "code" after, sodp[s.Length]becomes true.Final Answer:
True -> Option DQuick Check:
Substring segmentation found = True [OK]
- Assuming false because of substring indexing confusion
- Thinking code throws exception on substring
- Ignoring dp array meaning
bool WordBreak(string s, IList<string> wordDict) {
var dp = new bool[s.Length];
dp[0] = true;
for (int i = 1; i <= s.Length; i++) {
for (int j = 0; j < i; j++) {
if (dp[j] && wordDict.Contains(s.Substring(j, i - j))) {
dp[i] = true;
break;
}
}
}
return dp[s.Length];
}Solution
Step 1: Identify dp array size and indexing issue
The dp array should have length s.Length + 1 to represent empty substring at dp[0]. Current dp size is s.Length, causing index errors.Step 2: Fix dp initialization and size
Changing dp to size s.Length + 1 and setting dp[0] = true fixes indexing and logic.Final Answer:
Change dp array size to s.Length + 1 and initialize dp[0] = true -> Option AQuick Check:
dp size and initialization fix = Change dp array size to s.Length + 1 and initialize dp[0] = true [OK]
- Using dp size s.Length causes index out of range
- Incorrect substring parameters
- Removing inner loop breaks logic
s = "applepenapple" and dictionary wordDict = ["apple", "pen"], which of the following modifications to the Word Break algorithm will correctly count the number of ways to segment s into dictionary words instead of just checking if segmentation is possible?Solution
Step 1: Understand difference between boolean and count dp
Boolean dp tracks if segmentation is possible. To count ways, dp must store counts as integers.Step 2: Modify dp to int[], initialize dp[0] = 1, and accumulate counts
When substring s[j..i-1] is in dictionary, add dp[j] to dp[i] to count all ways.Final Answer:
Change dp array to int[], initialize dp[0] = 1, and update dp[i] += dp[j] when substring is in dictionary -> Option AQuick Check:
Counting ways requires int dp and accumulation [OK]
- Using boolean dp for counting ways
- Returning boolean as integer count
- Ignoring memoization in recursion
Ready to prove it?
Pass the test and this pattern moves unknown
