-
-
Notifications
You must be signed in to change notification settings - Fork 7.3k
/
Copy pathword_break.cpp
186 lines (169 loc) · 6.28 KB
/
word_break.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/**
* @file
* @brief [Word Break Problem](https://leetcode.com/problems/word-break/)
* @details
* Given a non-empty string s and a dictionary wordDict containing a list of
* non-empty words, determine if s can be segmented into a space-separated
* sequence of one or more dictionary words.
*
* Note:
* The same word in the dictionary may be reused multiple times in the
* segmentation. You may assume the dictionary does not contain duplicate words.
*
* Example 1:
* Input: s = "leetcode", wordDict = ["leet", "code"]
* Output: true
* Explanation: Return true because "leetcode" can be segmented as "leet code".
*
* Example 2:
* Input: s = "applepenapple", wordDict = ["apple", "pen"]
* Output: true
* Explanation: Return true because "applepenapple" can be segmented as "apple
* pen apple". Note that you are allowed to reuse a dictionary word.
*
* Example 3:
* Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
* Output: false
*
* @author [Akshay Anand] (https://github.com/axayjha)
*/
#include <cassert>
#include <climits>
#include <iostream>
#include <string>
#include <unordered_set>
#include <vector>
/**
* @namespace dynamic_programming
* @brief Dynamic programming algorithms
*/
namespace dynamic_programming {
/**
* @namespace word_break
* @brief Functions for [Word Break](https://leetcode.com/problems/word-break/)
* problem
*/
namespace word_break {
/**
* @brief Function that checks if the string passed in param is present in
* the the unordered_set passed
*
* @param str the string to be searched
* @param strSet unordered set of string, that is to be looked into
* @returns `true` if str is present in strSet
* @returns `false` if str is not present in strSet
*/
bool exists(const std::string &str,
const std::unordered_set<std::string> &strSet) {
return strSet.find(str) != strSet.end();
}
/**
* @brief Function that checks if the string passed in param can be
* segmented from position 'pos', and then correctly go on to segment the
* rest of the string correctly as well to reach a solution
*
* @param s the complete string to be segmented
* @param strSet unordered set of string, that is to be used as the
* reference dictionary
* @param pos the index value at which we will segment string and test
* further if it is correctly segmented at pos
* @param dp the vector to memoize solution for each position
* @returns `true` if a valid solution/segmentation is possible by segmenting at
* index pos
* @returns `false` otherwise
*/
bool check(const std::string &s, const std::unordered_set<std::string> &strSet,
int pos, std::vector<int> *dp) {
if (pos == s.length()) {
// if we have reached till the end of the string, means we have
// segmented throughout correctly hence we have a solution, thus
// returning true
return true;
}
if (dp->at(pos) != INT_MAX) {
// if dp[pos] is not INT_MAX, means we must have saved a solution
// for the position pos; then return if the solution at pos is true
// or not
return dp->at(pos) == 1;
}
std::string wordTillNow =
""; // string to save the prefixes of word till different positons
for (int i = pos; i < s.length(); i++) {
// Loop starting from pos to end, to check valid set of
// segmentations if any
wordTillNow +=
std::string(1, s[i]); // storing the prefix till the position i
// if the prefix till current position is present in the dictionary
// and the remaining substring can also be segmented legally, then
// set solution at position pos in the memo, and return true
if (exists(wordTillNow, strSet) && check(s, strSet, i + 1, dp)) {
dp->at(pos) = 1;
return true;
}
}
// if function has still not returned, then there must be no legal
// segmentation possible after segmenting at pos
dp->at(pos) = 0; // so set solution at pos as false
return false; // and return no solution at position pos
}
/**
* @brief Function that checks if the string passed in param can be
* segmented into the strings present in the vector.
* In others words, it checks if any permutation of strings in
* the vector can be concatenated to form the final string.
*
* @param s the complete string to be segmented
* @param wordDict a vector of words to be used as dictionary to look into
* @returns `true` if s can be formed by a combination of strings present in
* wordDict
* @return `false` otherwise
*/
bool wordBreak(const std::string &s, const std::vector<std::string> &wordDict) {
// unordered set to store words in the dictionary for constant time
// search
std::unordered_set<std::string> strSet;
for (const auto &s : wordDict) {
strSet.insert(s);
}
// a vector to be used for memoization, whose value at index i will
// tell if the string s can be segmented (correctly) at position i.
// initializing it with INT_MAX (which will denote no solution)
std::vector<int> dp(s.length(), INT_MAX);
// calling check method with position = 0, to check from left
// from where can be start segmenting the complete string in correct
// manner
return check(s, strSet, 0, &dp);
}
} // namespace word_break
} // namespace dynamic_programming
/**
* @brief Test implementations
* @returns void
*/
static void test() {
// the complete string
const std::string s = "applepenapple";
// the dictionary to be used
const std::vector<std::string> wordDict = {"apple", "pen"};
assert(dynamic_programming::word_break::wordBreak(s, wordDict));
// should return true, as applepenapple can be segmented as apple + pen +
// apple
std::cout << dynamic_programming::word_break::wordBreak(s, wordDict)
<< std::endl;
std::cout << "Test implementation passed!\n";
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // call the test function :)
// the complete string
const std::string s = "applepenapple";
// the dictionary to be used
const std::vector<std::string> wordDict = {"apple", "pen"};
// should return true, as applepenapple can be segmented as apple + pen +
// apple
std::cout << dynamic_programming::word_break::wordBreak(s, wordDict)
<< std::endl;
}