Word Break Problem using Backtracking
Given a non-empty sequence s and a dictionary dict[] containing a list of non-empty words, the task is to return all possible ways to break the sentence in individual dictionary words.
Note: The same word in the dictionary may be reused multiple times while breaking.
Examples:
Input: s = “catsanddog” , dict = [“cat”, “cats”, “and”, “sand”, “dog”]
Output:
“cats and dog”
“cat sand dog”
Explanation: The string is split into above 2 ways, in each way all are valid dictionary words.
Input: s = “pineapplepenapple” , dict = [“apple”, “pen”, “applepen”, “pine”, “pineapple”]
Output:
“pine apple pen apple”
“pineapple pen apple”
“pine applepen apple”
Explanation: The string is split into above 3 ways, in each way all are valid dictionary words.
Approach:
For the recursive approach, there are two cases at each step (the size of the string decreases in each step):
- Include the current substring in the solution, If the substring exists in the dictionary, recursively check for the rest of the string starting from the next index.
- Skip the current substring and move to the next possible substring starting from the same index.
wordBreak(s,start) = wordBreak(s, end) , if s[start:end] ∈ dictionary
Base Case : wordBreak(s, start) = true, this signifies that a valid sentence has been constructed for the given input string.
Steps to implement the above idea:
- Convert the dictionary into a hash set for quick search.
- If the start index reaches the length of the string (s), it signifies a valid sentence has been constructed. Add the current sentence (curr) to the result.
- Loop through every substring starting at start and ending at all possible positions (end).
- For each substring, check if it exists in the dictionary (dictSet).
- If valid:
- Append the word to the current sentence (curr).
- Recursively call the function for the remaining part of the string (from end onwards).
- After the recursive call returns, restore the state of curr to ensure the next branch of exploration starts with the correct sentence.
// C++ implementation to find valid word
// break using Recursion
#include <bits/stdc++.h>
using namespace std;
// Helper function to perform backtracking
void wordBreakHelper(string& s, unordered_set<string>& dictSet,
string& curr, vector<string>& res,
int start) {
// If start reaches the end of the string,
// save the result
if (start == s.length()) {
res.push_back(curr);
return;
}
// Try every possible substring from the current index
for (int end = start + 1; end <= s.length(); ++end) {
string word = s.substr(start, end - start);
// Check if the word exists in the dictionary
if (dictSet.count(word)) {
string prev = curr;
// Append the word to the current sentence
if (!curr.empty()) {
curr += " ";
}
curr += word;
// Recurse for the remaining string
wordBreakHelper(s, dictSet, curr, res, end);
// Backtrack to restore the current sentence
curr = prev;
}
}
}
// Main function to generate all possible sentence breaks
vector<string> wordBreak(string s, vector<string>& dict) {
// Convert dictionary vector
// to an unordered set
unordered_set<string>
dictSet(dict.begin(), dict.end());
vector<string> res;
string curr;
wordBreakHelper(s, dictSet, curr, res, 0);
return res;
}
int main() {
string s = "ilikesamsungmobile";
vector<string> dict = {"i", "like", "sam", "sung",
"samsung", "mobile", "ice",
"and", "cream", "icecream",
"man", "go", "mango"};
vector<string> result = wordBreak(s, dict);
for (string sentence : result) {
cout << sentence << endl;
}
return 0;
}
// Java implementation to find valid
// word break using Recursion
import java.util.*;
class GfG {
// Helper function to perform backtracking
static void wordBreakHelper(String s, HashSet<String> dictSet,
String curr, List<String> res,
int start) {
// If start reaches the end of the string,
// save the result
if (start == s.length()) {
res.add(curr);
return;
}
// Try every possible substring from the current index
for (int end = start + 1; end <= s.length(); ++end) {
String word = s.substring(start, end);
// Check if the word exists in the dictionary
if (dictSet.contains(word)) {
String prev = curr;
// Append the word to the current sentence
if (!curr.isEmpty()) {
curr += " ";
}
curr += word;
// Recurse for the remaining string
wordBreakHelper(s, dictSet, curr, res, end);
// Backtrack to restore the current sentence
curr = prev;
}
}
}
// Main function to generate all possible sentence breaks
static List<String> wordBreak(String s, List<String> dict) {
// Convert dictionary vector to a HashSet
HashSet<String> dictSet = new HashSet<>(dict);
List<String> res = new ArrayList<>();
String curr = "";
wordBreakHelper(s, dictSet, curr, res, 0);
return res;
}
public static void main(String[] args) {
String s = "ilikesamsungmobile";
List<String> dict = Arrays.asList("i", "like", "sam", "sung",
"samsung", "mobile", "ice",
"and", "cream", "icecream",
"man", "go", "mango");
List<String> result = wordBreak(s, dict);
for (String sentence : result) {
System.out.println(sentence);
}
}
}
# Python implementation to find valid
# word break using Recursion
def wordBreakHelper(s, dictSet, curr, res, start):
# If start reaches the end of the string,
# save the result
if start == len(s):
res.append(curr)
return
# Try every possible substring from the current index
for end in range(start + 1, len(s) + 1):
word = s[start:end]
# Check if the word exists in the dictionary
if word in dictSet:
prev = curr
# Append the word to the current sentence
if curr:
curr += " "
curr += word
# Recurse for the remaining string
wordBreakHelper(s, dictSet, curr, res, end)
# Backtrack to restore the current sentence
curr = prev
def wordBreak(s, dict):
# Convert dictionary list to a set
dictSet = set(dict)
res = []
curr = ""
wordBreakHelper(s, dictSet, curr, res, 0)
return res
if __name__=='__main__':
s = "ilikesamsungmobile"
dict = ["i", "like", "sam", "sung",
"samsung", "mobile", "ice",
"and", "cream", "icecream",
"man", "go", "mango"]
result = wordBreak(s, dict)
for sentence in result:
print(sentence)
// C# implementation to find valid word
// break using Recursion
using System;
using System.Collections.Generic;
class GfG {
// Helper function to perform backtracking
static void wordBreakHelper(string s, HashSet<string> dictSet,
ref string curr, ref List<string> res,
int start) {
// If start reaches the end of the string,
// save the result
if (start == s.Length) {
res.Add(curr);
return;
}
// Try every possible substring from the current index
for (int end = start + 1; end <= s.Length; ++end) {
string word = s.Substring(start, end - start);
// Check if the word exists in the dictionary
if (dictSet.Contains(word)) {
string prev = curr;
// Append the word to the current sentence
if (curr.Length > 0) {
curr += " ";
}
curr += word;
// Recurse for the remaining string
wordBreakHelper(s, dictSet, ref curr,
ref res, end);
// Backtrack to restore the current sentence
curr = prev;
}
}
}
// Main function to generate all possible sentence breaks
static List<string> wordBreak(string s,
List<string> dict) {
// Convert dictionary list to a HashSet
HashSet<string> dictSet
= new HashSet<string>(dict);
List<string> res = new List<string>();
string curr = "";
wordBreakHelper(s, dictSet, ref curr, ref res, 0);
return res;
}
static void Main() {
string s = "ilikesamsungmobile";
List<string> dict
= new List<string> {"i", "like", "sam", "sung",
"samsung", "mobile", "ice",
"and", "cream", "icecream",
"man", "go", "mango"};
List<string> result = wordBreak(s, dict);
foreach (string sentence in result) {
Console.WriteLine(sentence);
}
}
}
// JavaScript implementation to find valid
// word break using Recursion
// Helper function to perform backtracking
function wordBreakHelper(s, dictSet, curr, res, start) {
// If start reaches the end of the string, save the result
if (start === s.length) {
res.push(curr);
return;
}
// Try every possible substring from the current index
for (let end = start + 1; end <= s.length; ++end) {
let word = s.substring(start, end);
// Check if the word exists in the dictionary
if (dictSet.has(word)) {
let prev = curr;
// Append the word to the current sentence
if (curr.length > 0) {
curr += " ";
}
curr += word;
// Recurse for the remaining string
wordBreakHelper(s, dictSet, curr, res, end);
// Backtrack to restore the current sentence
curr = prev;
}
}
}
// Main function to generate all possible sentence breaks
function wordBreak(s, dict) {
// Convert dictionary array to a Set
let dictSet = new Set(dict);
let res = [];
let curr = "";
wordBreakHelper(s, dictSet, curr, res, 0);
return res;
}
let s = "ilikesamsungmobile";
let dict = ["i", "like", "sam", "sung",
"samsung", "mobile", "ice",
"and", "cream", "icecream",
"man", "go", "mango"];
let result = wordBreak(s, dict);
result.forEach((sentence) => {
console.log(sentence);
});
Output
i like sam sung mobile i like samsung mobile
Time Complexity: O((2^n) * k), for a string of length n, there are 2^n possible partitions, and each substring check takes O(k) time (average substring length k), leading to O((2^n) * k).
Auxiliary Space: O(n), due to the recursion stack can go as deep as O(n) in the worst case.