package com.interview.dynamic; import java.util.*; /** * Date 08/01/2014 * @author tusroy * * Given a string and a dictionary, split this string into multiple words such that * each word belongs in dictionary. * * e.g peanutbutter -> pea nut butter * e.g Iliketoplay -> I like to play * * Solution * DP solution to this problem * if( input[i...j] belongs in dictionary) T[i][j] = i * else{ * T[i][j] = k if T[i][k-1] != -1 && T[k][j] != -1 * * Test cases * 1) Empty string * 2) String where entire string is in dictionary * 3) String which cannot be split into words which are in dictionary * 3) String which can be split into words which are in dictionary * */ public class BreakMultipleWordsWithNoSpaceIntoSpace { /** * Recursive and slow version of breaking word problem. * If no words can be formed it returns null */ public String breakWord(char[] str,int low,Set<String> dictionary){ StringBuffer buff = new StringBuffer(); for(int i= low; i < str.length; i++){ buff.append(str[i]); if(dictionary.contains(buff.toString())){ String result = breakWord(str, i+1, dictionary); if(result != null){ return buff.toString() + " " + result; } } } if(dictionary.contains(buff.toString())){ return buff.toString(); } return null; } /** * Dynamic programming version for breaking word problem. * It returns null string if string cannot be broken into multipe words * such that each word is in dictionary. * Gives preference to longer words over splits * e.g peanutbutter with dict{pea nut butter peanut} it would result in * peanut butter instead of pea nut butter. */ public String breakWordDP(String word, Set<String> dict){ int T[][] = new int[word.length()][word.length()]; for(int i=0; i < T.length; i++){ for(int j=0; j < T[i].length ; j++){ T[i][j] = -1; //-1 indicates string between i to j cannot be split } } //fill up the matrix in bottom up manner for(int l = 1; l <= word.length(); l++){ for(int i=0; i < word.length() -l + 1 ; i++){ int j = i + l-1; String str = word.substring(i,j+1); //if string between i to j is in dictionary T[i][j] is true if(dict.contains(str)){ T[i][j] = i; continue; } //find a k between i+1 to j such that T[i][k-1] && T[k][j] are both true for(int k=i+1; k <= j; k++){ if(T[i][k-1] != -1 && T[k][j] != -1){ T[i][j] = k; break; } } } } if(T[0][word.length()-1] == -1){ return null; } //create space separate word from string is possible StringBuffer buffer = new StringBuffer(); int i = 0; int j = word.length() -1; while(i < j){ int k = T[i][j]; if(i == k){ buffer.append(word.substring(i, j+1)); break; } buffer.append(word.substring(i,k) + " "); i = k; } return buffer.toString(); } /** * Prints all the words possible instead of just one combination. * Reference * https://leetcode.com/problems/word-break-ii/ */ public List<String> wordBreakTopDown(String s, Set<String> wordDict) { Map<Integer, List<String>> dp = new HashMap<>(); int max = 0; for (String s1 : wordDict) { max = Math.max(max, s1.length()); } return wordBreakUtil(s, wordDict, dp, 0, max); } private List<String> wordBreakUtil(String s, Set<String> dict, Map<Integer, List<String>> dp, int start, int max) { if (start == s.length()) { return Collections.singletonList(""); } if (dp.containsKey(start)) { return dp.get(start); } List<String> words = new ArrayList<>(); for (int i = start; i < start + max && i < s.length(); i++) { String newWord = s.substring(start, i + 1); if (!dict.contains(newWord)) { continue; } List<String> result = wordBreakUtil(s, dict, dp, i + 1, max); for (String word : result) { String extraSpace = word.length() == 0 ? "" : " "; words.add(newWord + extraSpace + word); } } dp.put(start, words); return words; } /** * Check if any one solution exists. * https://leetcode.com/problems/word-break/ */ public boolean wordBreakTopDownOneSolution(String s, Set<String> wordDict) { Map<Integer, Boolean> dp = new HashMap<>(); int max = 0; for (String s1 : wordDict) { max = Math.max(max, s1.length()); } return wordBreakTopDownOneSolutionUtil(s, wordDict, 0, max, dp); } private boolean wordBreakTopDownOneSolutionUtil(String s, Set<String> dict, int start, int max, Map<Integer, Boolean> dp) { if (start == s.length()) { return true; } if (dp.containsKey(start)) { return dp.get(start); } for (int i = start; i < start + max && i < s.length(); i++) { String newWord = s.substring(start, i + 1); if (!dict.contains(newWord)) { continue; } if (wordBreakTopDownOneSolutionUtil(s, dict, i + 1, max, dp)) { dp.put(start, true); return true; } } dp.put(start, false); return false; } public boolean wordBreakBottomUp(String s, List<String> wordList) { boolean[] T = new boolean[s.length() + 1]; Set<String> set = new HashSet<>(); for (String word : wordList) { set.add(word); } T[0] = true; for (int i = 1; i <= s.length(); i++) { for (int j = 0; j < i; j++) { if(T[j] && set.contains(s.substring(j, i))) { T[i] = true; break; } } } return T[s.length()]; } public static void main(String args[]){ Set<String> dictionary = new HashSet<String>(); dictionary.add("I"); dictionary.add("like"); dictionary.add("had"); dictionary.add("play"); dictionary.add("to"); String str = "Ihadliketoplay"; BreakMultipleWordsWithNoSpaceIntoSpace bmw = new BreakMultipleWordsWithNoSpaceIntoSpace(); String result1 = bmw.breakWordDP(str, dictionary); System.out.print(result1); } }