-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add post '(Leetcode) 211 - Design Add and Search Words Data Structure…
… (Trie)'
- Loading branch information
1 parent
65c10b6
commit 9eee1b9
Showing
2 changed files
with
219 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
--- | ||
layout: post | ||
title: (Leetcode) 211 - Design Add and Search Words Data Structure (Trie) | ||
categories: [스터디-알고리즘] | ||
tags: [자바, java, 리트코드, Leetcode, 알고리즘, brute force, trie, tree] | ||
date: 2024-06-30 00:30:00 +0900 | ||
toc: true | ||
--- | ||
|
||
기회가 되어 [달레님의 스터디](https://github.com/DaleStudy/leetcode-study)에 참여하여 시간이 될 때마다 한문제씩 풀어보고 있다. | ||
|
||
[https://neetcode.io/practice](https://neetcode.io/practice) | ||
|
||
--- | ||
|
||
[https://leetcode.com/problems/design-add-and-search-words-data-structure](https://leetcode.com/problems/design-add-and-search-words-data-structure) | ||
|
||
점진적으로 개선하여 보았다. | ||
|
||
## brute force | ||
|
||
아슬아슬하게 통과한다. | ||
|
||
```java | ||
class WordDictionary { | ||
|
||
Set<String> wordSet; | ||
|
||
public WordDictionary() { | ||
wordSet = new HashSet<>(); | ||
} | ||
|
||
public void addWord(String word) { | ||
wordSet.add(word); | ||
} | ||
|
||
public boolean search(String word) { | ||
Deque<String> queue = new ArrayDeque<>(); | ||
queue.push(word); | ||
|
||
while (queue.getFirst().contains(".")) { | ||
String _word = queue.removeFirst(); | ||
String pre = _word.substring(0, _word.indexOf(".")); | ||
String post = _word.substring(_word.indexOf(".") + 1); | ||
|
||
for (char c = 'a'; c <= 'z'; c++) { | ||
queue.addLast(pre + c + post); | ||
} | ||
} | ||
|
||
while (!queue.isEmpty()) { | ||
String _word = queue.removeFirst(); | ||
if (wordSet.contains(_word)) { | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
} | ||
``` | ||
|
||
### TC, SC | ||
|
||
- `.` 이 없을 때 | ||
- 시간 복잡도 : `O(1)` | ||
- 공간 복잡도 : `O(1)` | ||
- `.` 이 있을 때 | ||
- 시간 복잡도 : `O(26^N)` | ||
- 공간 복잡도 : `O(26^N)` | ||
- 여기서 N은 `.` 의 수 | ||
|
||
## trie | ||
|
||
[208. Implement Trie (Prefix Tree)](https://leetcode.com/problems/implement-trie-prefix-tree/) 문제 에서 사용한 Trie 재사용. | ||
|
||
```java | ||
class WordDictionary { | ||
|
||
Trie trie; // Trie 구현은 생략 | ||
|
||
public WordDictionary() { | ||
trie = new Trie(); | ||
} | ||
|
||
public void addWord(String word) { | ||
trie.insert(word); | ||
} | ||
|
||
public boolean search(String word) { | ||
if (word.contains(".")) { | ||
String pre = word.substring(0, word.indexOf(".")); | ||
String post = word.substring(word.indexOf(".") + 1); | ||
|
||
if (trie.startsWith(pre)) { | ||
for (char c = 'a'; c <= 'z'; c++) { | ||
if (search(pre + c + post)) { | ||
return true; | ||
} | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
return trie.search(word); | ||
} | ||
|
||
|
||
} | ||
``` | ||
|
||
### TC, SC | ||
|
||
입력된 문자열의 길이를 `L`, `.` 의 수를 `N` 이라고 하였을 때 | ||
|
||
addWord 메소드의 경우 시간 복잡도는 `O(L)`이다. | ||
search 메소드의 경우 입력된 문자열의 길이를 n 이라 하였을 때 시간 복잡도는 `O(L * 26 ^ N)`이다. | ||
|
||
공간 복잡도는 Trie 구조를 만드는데 사용된 공간이다. `insert된 문자열 길이의 평균` 를 `avg(L)`이라고 하였을 때 `O(avg(L) * 26)`이다. 26은 계수이기 때문에 생략할 수 있다. | ||
|
||
## trie 개선 | ||
|
||
이 문제에 적합하도록 search를 수정하였다. | ||
|
||
```java | ||
class WordDictionary { | ||
|
||
Trie trie; | ||
|
||
public WordDictionary() { | ||
trie = new Trie(); | ||
} | ||
|
||
public void addWord(String word) { | ||
trie.insert(word); | ||
} | ||
|
||
public boolean search(String word) { | ||
return trie.search(word); | ||
} | ||
|
||
|
||
} | ||
|
||
class Trie { | ||
|
||
Node root = new Node(); | ||
|
||
public Trie() { | ||
|
||
} | ||
|
||
public void insert(String word) { | ||
Node currentNode = root; | ||
for (char c : word.toCharArray()) { | ||
if (currentNode.nodes[c - 97] == null) { | ||
currentNode.nodes[c - 97] = new Node(); | ||
} | ||
currentNode = currentNode.nodes[c - 97]; | ||
} | ||
currentNode.val = word; | ||
} | ||
|
||
public boolean search(String word) { | ||
return search(root, word, 0); | ||
} | ||
|
||
public boolean search(Node node, String word, int index) { | ||
if (node == null) { | ||
return false; | ||
} | ||
|
||
if (node.val != null && node.val.length() == word.length()) { | ||
return true; | ||
} | ||
|
||
if (index >= word.length()) { | ||
return false; | ||
} | ||
|
||
char c = word.charAt(index); | ||
|
||
if (c == '.') { | ||
for (char _c = 'a'; _c <= 'z'; _c++) { | ||
if (search(node.nodes[_c - 97], word, index + 1)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} else if (node.nodes[c - 97] == null) { | ||
return false; | ||
} | ||
|
||
return search(node.nodes[c - 97], word, index + 1); | ||
} | ||
} | ||
|
||
class Node { | ||
String val; | ||
Node[] nodes = new Node[26]; | ||
} | ||
``` | ||
|
||
### TC, SC | ||
|
||
입력된 문자열의 길이를 `L`, `.` 의 수를 `N` 이라고 하였을 때 | ||
|
||
addWord 메소드의 경우 시간 복잡도는 `O(L)`이다. | ||
search 메소드의 경우 입력된 문자열의 길이를 n 이라 하였을 때 시간 복잡도는 `O(L * 26 ^ N)`이다. | ||
개선 전과 비교해봤을 때 표기상으로는 차이가 없으나, 불필요한 과정을 제거하게되어서 시간이 매우 단축된다. (`trie.startsWith(pre)`이 사라졌고, search의 호출 횟수가 줄어듬.) | ||
|
||
공간 복잡도는 Trie 구조를 만드는데 사용된 공간이다. `insert된 문자열 길이의 평균` 를 `avg(L)`이라고 하였을 때 `O(avg(L) * 26)`이다. 26은 계수이기 때문에 생략할 수 있다. | ||
|
||
### submit result | ||
|
||
개선된 trie 로직으로 제출결과 최상단에 위치하였다. (약 beats 95%) | ||
|
||
![submit result](/assets/images/2024-06-30-leetcode-211/submit_result.png) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.