|
5 | 5 | ## 题目描述
|
6 | 6 |
|
7 | 7 | <!-- 这里写题目描述 -->
|
| 8 | + |
8 | 9 | <p>给定一份单词的清单,设计一个算法,创建由字母组成的面积最大的矩形,其中每一行组成一个单词(自左向右),每一列也组成一个单词(自上而下)。不要求这些单词在清单里连续出现,但要求所有行等长,所有列等高。</p>
|
9 | 10 | <p>如果有多个面积最大的矩形,输出任意一个均可。一个单词可以重复使用。</p>
|
10 | 11 | <p><strong>示例 1:</strong></p>
|
|
28 | 29 | ## 解法
|
29 | 30 |
|
30 | 31 | <!-- 这里可写通用的实现逻辑 -->
|
| 32 | + |
| 33 | +**方法一:分组 + 回溯 + 字典树** |
| 34 | + |
| 35 | +我们注意到,构建单词矩阵时所用的单词长度是相同的,因此,我们可以将单词按照长度分组,记录在哈希表 $d$ 中。对于每个长度,我们只需要考虑该长度的单词即可。 |
| 36 | + |
| 37 | +我们使用回溯的方法来构建单词矩阵。我们使用一个列表 $t$ 来记录当前已经构建好的单词矩阵,列表中的每个单词都具有相同的长度。我们从哈希表 $d$ 中取出长度为 $n$ 的单词列表,从中选择一个单词 $w$,加入到 $t$ 中。如果此时不是一个合法的单词矩阵,那么我们就不继续往下搜索,而是尝试选择另一个单词。如果是一个合法的单词矩阵,并且已经构建完成,那么我们更新最大面积以及答案矩阵;然后,我们递归地进行搜索,寻找下一个单词。最后,我们将 $w$ 从 $t$ 中移除,进入下一轮搜索。 |
| 38 | + |
| 39 | +在判断单词矩阵是否合法时,我们可以使用字典树来进行优化。我们将所有的单词加入到字典树中,然后对于每一列,我们检查其是否是一个单词。如果是一个单词,那么我们就检查下一列,否则我们就可以停止对该单词矩阵的搜索了。 |
| 40 | + |
31 | 41 | <!-- tabs:start -->
|
32 | 42 |
|
33 | 43 | ### **Python3**
|
34 | 44 |
|
35 | 45 | <!-- 这里可写当前语言的特殊实现逻辑 -->
|
36 | 46 |
|
37 | 47 | ```python
|
| 48 | +class Trie: |
| 49 | + def __init__(self): |
| 50 | + self.children = [None] * 26 |
| 51 | + self.is_end = False |
| 52 | + |
| 53 | + def insert(self, w): |
| 54 | + node = self |
| 55 | + for c in w: |
| 56 | + idx = ord(c) - ord("a") |
| 57 | + if node.children[idx] is None: |
| 58 | + node.children[idx] = Trie() |
| 59 | + node = node.children[idx] |
| 60 | + node.is_end = True |
| 61 | + |
38 | 62 |
|
| 63 | +class Solution: |
| 64 | + def maxRectangle(self, words: List[str]) -> List[str]: |
| 65 | + def check(mat): |
| 66 | + m, n = len(mat), len(mat[0]) |
| 67 | + ans = 1 |
| 68 | + for j in range(n): |
| 69 | + node = trie |
| 70 | + for i in range(m): |
| 71 | + idx = ord(mat[i][j]) - ord("a") |
| 72 | + if node.children[idx] is None: |
| 73 | + return 0 |
| 74 | + node = node.children[idx] |
| 75 | + if not node.is_end: |
| 76 | + ans = 2 |
| 77 | + return ans |
| 78 | + |
| 79 | + def dfs(ws): |
| 80 | + nonlocal ans, max_s, max_l |
| 81 | + if len(ws[0]) * max_l <= max_s or len(t) >= max_l: |
| 82 | + return |
| 83 | + |
| 84 | + for w in ws: |
| 85 | + t.append(w) |
| 86 | + st = check(t) |
| 87 | + if st == 0: |
| 88 | + t.pop() |
| 89 | + continue |
| 90 | + if st == 1 and max_s < len(t) * len(t[0]): |
| 91 | + ans = t[:] |
| 92 | + max_s = len(t) * len(t[0]) |
| 93 | + dfs(ws) |
| 94 | + t.pop() |
| 95 | + |
| 96 | + d = defaultdict(list) |
| 97 | + trie = Trie() |
| 98 | + max_l = 0 |
| 99 | + for w in words: |
| 100 | + trie.insert(w) |
| 101 | + max_l = max(max_l, len(w)) |
| 102 | + d[len(w)].append(w) |
| 103 | + |
| 104 | + max_s = 0 |
| 105 | + ans = [] |
| 106 | + for ws in d.values(): |
| 107 | + t = [] |
| 108 | + dfs(ws) |
| 109 | + return ans |
39 | 110 | ```
|
40 | 111 |
|
41 | 112 | ### **Java**
|
42 | 113 |
|
43 | 114 | <!-- 这里可写当前语言的特殊实现逻辑 -->
|
44 | 115 |
|
45 | 116 | ```java
|
| 117 | +class Trie { |
| 118 | + Trie[] children = new Trie[26]; |
| 119 | + boolean isEnd; |
| 120 | + |
| 121 | + void insert(String word) { |
| 122 | + Trie node = this; |
| 123 | + for (char c : word.toCharArray()) { |
| 124 | + c -= 'a'; |
| 125 | + if (node.children[c] == null) { |
| 126 | + node.children[c] = new Trie(); |
| 127 | + } |
| 128 | + node = node.children[c]; |
| 129 | + } |
| 130 | + node.isEnd = true; |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +class Solution { |
| 135 | + private int maxL; |
| 136 | + private int maxS; |
| 137 | + private String[] ans; |
| 138 | + private Trie trie = new Trie(); |
| 139 | + private List<String> t = new ArrayList<>(); |
| 140 | + |
| 141 | + public String[] maxRectangle(String[] words) { |
| 142 | + Map<Integer, List<String>> d = new HashMap<>(100); |
| 143 | + for (String w : words) { |
| 144 | + maxL = Math.max(maxL, w.length()); |
| 145 | + trie.insert(w); |
| 146 | + d.computeIfAbsent(w.length(), k -> new ArrayList<>()).add(w); |
| 147 | + } |
| 148 | + for (List<String> ws : d.values()) { |
| 149 | + t.clear(); |
| 150 | + dfs(ws); |
| 151 | + } |
| 152 | + return ans; |
| 153 | + } |
| 154 | + |
| 155 | + private void dfs(List<String> ws) { |
| 156 | + if (ws.get(0).length() * maxL <= maxS || t.size() >= maxL) { |
| 157 | + return; |
| 158 | + } |
| 159 | + for (String w : ws) { |
| 160 | + t.add(w); |
| 161 | + int st = check(t); |
| 162 | + if (st == 0) { |
| 163 | + t.remove(t.size() - 1); |
| 164 | + continue; |
| 165 | + } |
| 166 | + if (st == 1 && maxS < t.size() * t.get(0).length()) { |
| 167 | + maxS = t.size() * t.get(0).length(); |
| 168 | + ans = t.toArray(new String[0]); |
| 169 | + } |
| 170 | + dfs(ws); |
| 171 | + t.remove(t.size() - 1); |
| 172 | + } |
| 173 | + } |
| 174 | + |
| 175 | + private int check(List<String> mat) { |
| 176 | + int m = mat.size(), n = mat.get(0).length(); |
| 177 | + int ans = 1; |
| 178 | + for (int j = 0; j < n; ++j) { |
| 179 | + Trie node = trie; |
| 180 | + for (int i = 0; i < m; ++i) { |
| 181 | + int idx = mat.get(i).charAt(j) - 'a'; |
| 182 | + if (node.children[idx] == null) { |
| 183 | + return 0; |
| 184 | + } |
| 185 | + node = node.children[idx]; |
| 186 | + } |
| 187 | + if (!node.isEnd) { |
| 188 | + ans = 2; |
| 189 | + } |
| 190 | + } |
| 191 | + return ans; |
| 192 | + } |
| 193 | +} |
| 194 | +``` |
| 195 | + |
| 196 | +### **C++** |
| 197 | + |
| 198 | +```cpp |
| 199 | +class Trie { |
| 200 | +public: |
| 201 | + vector<Trie*> children; |
| 202 | + bool is_end; |
| 203 | + |
| 204 | + Trie() { |
| 205 | + children = vector<Trie*>(26, nullptr); |
| 206 | + is_end = false; |
| 207 | + } |
| 208 | + |
| 209 | + void insert(const string& word) { |
| 210 | + Trie* cur = this; |
| 211 | + for (char c : word) { |
| 212 | + c -= 'a'; |
| 213 | + if (cur->children[c] == nullptr) { |
| 214 | + cur->children[c] = new Trie; |
| 215 | + } |
| 216 | + cur = cur->children[c]; |
| 217 | + } |
| 218 | + cur->is_end = true; |
| 219 | + } |
| 220 | +}; |
| 221 | + |
| 222 | +class Solution { |
| 223 | +public: |
| 224 | + vector<string> maxRectangle(vector<string>& words) { |
| 225 | + unordered_map<int, vector<string>> d; |
| 226 | + int maxL = 0, maxS = 0; |
| 227 | + vector<string> ans; |
| 228 | + vector<string> t; |
| 229 | + Trie* trie = new Trie(); |
| 230 | + for (auto& w : words) { |
| 231 | + maxL = max(maxL, (int) w.size()); |
| 232 | + d[w.size()].emplace_back(w); |
| 233 | + trie->insert(w); |
| 234 | + } |
| 235 | + auto check = [&](vector<string>& mat) { |
| 236 | + int m = mat.size(), n = mat[0].size(); |
| 237 | + int ans = 1; |
| 238 | + for (int j = 0; j < n; ++j) { |
| 239 | + Trie* node = trie; |
| 240 | + for (int i = 0; i < m; ++i) { |
| 241 | + int idx = mat[i][j] - 'a'; |
| 242 | + if (!node->children[idx]) { |
| 243 | + return 0; |
| 244 | + } |
| 245 | + node = node->children[idx]; |
| 246 | + } |
| 247 | + if (!node->is_end) { |
| 248 | + ans = 2; |
| 249 | + } |
| 250 | + } |
| 251 | + return ans; |
| 252 | + }; |
| 253 | + |
| 254 | + function<void(vector<string>&)> dfs = [&](vector<string>& ws) { |
| 255 | + if (ws[0].size() * maxL <= maxS || t.size() >= maxL) { |
| 256 | + return; |
| 257 | + } |
| 258 | + for (auto& w : ws) { |
| 259 | + t.emplace_back(w); |
| 260 | + int st = check(t); |
| 261 | + if (st == 0) { |
| 262 | + t.pop_back(); |
| 263 | + continue; |
| 264 | + } |
| 265 | + if (st == 1 && maxS < t.size() * t[0].size()) { |
| 266 | + maxS = t.size() * t[0].size(); |
| 267 | + ans = t; |
| 268 | + } |
| 269 | + dfs(ws); |
| 270 | + t.pop_back(); |
| 271 | + } |
| 272 | + }; |
| 273 | + for (auto& [_, ws] : d) { |
| 274 | + t.clear(); |
| 275 | + dfs(ws); |
| 276 | + } |
| 277 | + return ans; |
| 278 | + } |
| 279 | +}; |
| 280 | +``` |
| 281 | +
|
| 282 | +### **Go** |
| 283 | +
|
| 284 | +```go |
| 285 | +type Trie struct { |
| 286 | + children [26]*Trie |
| 287 | + isEnd bool |
| 288 | +} |
| 289 | +
|
| 290 | +func newTrie() *Trie { |
| 291 | + return &Trie{} |
| 292 | +} |
| 293 | +func (this *Trie) insert(word string) { |
| 294 | + node := this |
| 295 | + for _, c := range word { |
| 296 | + c -= 'a' |
| 297 | + if node.children[c] == nil { |
| 298 | + node.children[c] = newTrie() |
| 299 | + } |
| 300 | + node = node.children[c] |
| 301 | + } |
| 302 | + node.isEnd = true |
| 303 | +} |
| 304 | +
|
| 305 | +func maxRectangle(words []string) (ans []string) { |
| 306 | + trie := newTrie() |
| 307 | + d := map[int][]string{} |
| 308 | + t := []string{} |
| 309 | + var maxL, maxS int |
| 310 | + for _, w := range words { |
| 311 | + maxL = max(maxL, len(w)) |
| 312 | + d[len(w)] = append(d[len(w)], w) |
| 313 | + trie.insert(w) |
| 314 | + } |
| 315 | + check := func(mat []string) int { |
| 316 | + m, n := len(mat), len(mat[0]) |
| 317 | + ans := 1 |
| 318 | + for j := 0; j < n; j++ { |
| 319 | + node := trie |
| 320 | + for i := 0; i < m; i++ { |
| 321 | + idx := mat[i][j] - 'a' |
| 322 | + if node.children[idx] == nil { |
| 323 | + return 0 |
| 324 | + } |
| 325 | + node = node.children[idx] |
| 326 | + } |
| 327 | + if !node.isEnd { |
| 328 | + ans = 2 |
| 329 | + } |
| 330 | + } |
| 331 | + return ans |
| 332 | + } |
| 333 | + var dfs func([]string) |
| 334 | + dfs = func(ws []string) { |
| 335 | + if len(ws[0])*maxL <= maxS || len(t) >= maxL { |
| 336 | + return |
| 337 | + } |
| 338 | + for _, w := range ws { |
| 339 | + t = append(t, w) |
| 340 | + st := check(t) |
| 341 | + if st == 0 { |
| 342 | + t = t[:len(t)-1] |
| 343 | + continue |
| 344 | + } |
| 345 | + if st == 1 && maxS < len(t)*len(t[0]) { |
| 346 | + maxS = len(t) * len(t[0]) |
| 347 | + ans = append([]string{}, t...) |
| 348 | + } |
| 349 | + dfs(ws) |
| 350 | + t = t[:len(t)-1] |
| 351 | + } |
| 352 | + } |
| 353 | + for _, ws := range d { |
| 354 | + dfs(ws) |
| 355 | + } |
| 356 | + return |
| 357 | +} |
46 | 358 |
|
| 359 | +func max(a, b int) int { |
| 360 | + if a > b { |
| 361 | + return a |
| 362 | + } |
| 363 | + return b |
| 364 | +} |
47 | 365 | ```
|
48 | 366 |
|
49 | 367 | ### **...**
|
|
0 commit comments