diff --git a/solution/0500-0599/0527.Word Abbreviation/README.md b/solution/0500-0599/0527.Word Abbreviation/README.md index ef9606027a9d5..11a1873de0929 100644 --- a/solution/0500-0599/0527.Word Abbreviation/README.md +++ b/solution/0500-0599/0527.Word Abbreviation/README.md @@ -47,97 +47,71 @@ -**方法一:前缀树** +**方法一:分组字典树** -将 $words$ 按照长度分组,构造对应长度的前缀树。 +我们注意到,如果两个单词的缩写相同,那么它们的首尾字母一定相同,并且它们的长度一定相同。因此,我们可以将所有的单词按照长度以及末尾字母进行分组,对于每组单词,我们使用字典树存储这组单词的信息。 - +字典树的每个节点结构如下: -### **Python3** +- `children`:长度为 $26$ 的数组,表示该节点的所有子节点。 +- `cnt`:表示经过该节点的单词数量。 - +对于每个单词,我们将其插入到字典树中,同时记录每个节点的 `cnt` 值。 -```python -class Trie: - def __init__(self): - self.children = [None] * 26 - self.v = defaultdict(int) +在查询时,我们从根节点开始,对于当前的字母,如果其对应的子节点的 `cnt` 值为 $1$,那么我们就找到了唯一的缩写,我们返回当前前缀的长度即可。否则,我们继续向下遍历。遍历结束后,如果我们没有找到唯一的缩写,那么我们返回原单词的长度。在得到所有单词的前缀长度后,我们判断单词的缩写是否比原单词更短,如果是,那么我们将其加入答案中,否则我们将原单词加入答案中。 - def insert(self, w): - node = self - for c in w: - idx = ord(c) - ord('a') - if node.children[idx] is None: - node.children[idx] = Trie() - node = node.children[idx] - node.v[(w[-1], len(w))] += 1 +时间复杂度 $O(L)$,空间复杂度 $O(L)$,其中 $L$ 为所有单词的长度和。 - def search(self, w): - node = self - res = [] - for c in w[:-1]: - idx = ord(c) - ord('a') - node = node.children[idx] - res.append(c) - if node.v[(w[-1], len(w))] == 1: - break - n = len(w) - len(res) - 1 - if n: - res.append(str(n)) - res.append(w[-1]) - t = ''.join(res) - return t if len(t) < len(w) else w + +### **Python3** -class Solution: - def wordsAbbreviation(self, words: List[str]) -> List[str]: - trie = Trie() - for w in words: - trie.insert(w) - return [trie.search(w) for w in words] -``` + ```python class Trie: + __slots__ = ["children", "cnt"] + def __init__(self): self.children = [None] * 26 - self.v = Counter() + self.cnt = 0 - def insert(self, w): + def insert(self, w: str): node = self for c in w: - idx = ord(c) - ord('a') - if node.children[idx] is None: + idx = ord(c) - ord("a") + if not node.children[idx]: node.children[idx] = Trie() node = node.children[idx] - node.v[w[-1]] += 1 + node.cnt += 1 - def search(self, w): + def search(self, w: str) -> int: node = self - res = [] - for c in w[:-1]: - idx = ord(c) - ord('a') + cnt = 0 + for c in w: + cnt += 1 + idx = ord(c) - ord("a") node = node.children[idx] - res.append(c) - if node.v[w[-1]] == 1: - break - n = len(w) - len(res) - 1 - if n: - res.append(str(n)) - res.append(w[-1]) - t = ''.join(res) - return t if len(t) < len(w) else w + if node.cnt == 1: + return cnt + return len(w) class Solution: def wordsAbbreviation(self, words: List[str]) -> List[str]: - trees = {} + tries = {} for w in words: - if len(w) not in trees: - trees[len(w)] = Trie() + m = len(w) + if (m, w[-1]) not in tries: + tries[(m, w[-1])] = Trie() + tries[(m, w[-1])].insert(w) + ans = [] for w in words: - trees[len(w)].insert(w) - return [trees[len(w)].search(w) for w in words] + cnt = tries[(len(w), w[-1])].search(w) + ans.append( + w if cnt + 2 >= len(w) else w[:cnt] + str(len(w) - cnt - 1) + w[-1] + ) + return ans ``` ### **Java** @@ -146,124 +120,235 @@ class Solution: ```java class Trie { - Trie[] children = new Trie[26]; - int[] v = new int[26]; + private final Trie[] children = new Trie[26]; + private int cnt; - void insert(String w) { + public void insert(String w) { Trie node = this; - int t = w.charAt(w.length() - 1) - 'a'; for (char c : w.toCharArray()) { - c -= 'a'; - if (node.children[c] == null) { - node.children[c] = new Trie(); + int idx = c - 'a'; + if (node.children[idx] == null) { + node.children[idx] = new Trie(); } - node = node.children[c]; - node.v[t]++; + node = node.children[idx]; + ++node.cnt; } } - String search(String w) { + public int search(String w) { Trie node = this; - StringBuilder res = new StringBuilder(); - int t = w.charAt(w.length() - 1) - 'a'; - for (int i = 0; i < w.length() - 1; ++i) { - char c = w.charAt(i); - node = node.children[c - 'a']; - res.append(c); - if (node.v[t] == 1) { - break; + int ans = 0; + for (char c : w.toCharArray()) { + ++ans; + int idx = c - 'a'; + node = node.children[idx]; + if (node.cnt == 1) { + return ans; } } - int n = w.length() - res.length() - 1; - if (n > 0) { - res.append(n); - } - res.append(w.charAt(w.length() - 1)); - return res.length() < w.length() ? res.toString() : w; + return w.length(); } } class Solution { public List wordsAbbreviation(List words) { - Map trees = new HashMap<>(); - for (String w : words) { - if (!trees.containsKey(w.length())) { - trees.put(w.length(), new Trie()); - } - } - for (String w : words) { - trees.get(w.length()).insert(w); + Map, Trie> tries = new HashMap<>(); + for (var w : words) { + var key = List.of(w.length(), w.charAt(w.length() - 1) - 'a'); + tries.putIfAbsent(key, new Trie()); + tries.get(key).insert(w); } List ans = new ArrayList<>(); - for (String w : words) { - ans.add(trees.get(w.length()).search(w)); + for (var w : words) { + int m = w.length(); + var key = List.of(m, w.charAt(m - 1) - 'a'); + int cnt = tries.get(key).search(w); + ans.add(cnt + 2 >= m ? w : w.substring(0, cnt) + (m - cnt - 1) + w.substring(m - 1)); } return ans; } } ``` +### **C++** + +```cpp +class Trie { +public: + Trie() + : cnt(0) { + fill(children.begin(), children.end(), nullptr); + } + + void insert(const string& w) { + Trie* node = this; + for (char c : w) { + int idx = c - 'a'; + if (node->children[idx] == nullptr) { + node->children[idx] = new Trie(); + } + node = node->children[idx]; + ++node->cnt; + } + } + + int search(const string& w) { + Trie* node = this; + int ans = 0; + for (char c : w) { + ++ans; + int idx = c - 'a'; + node = node->children[idx]; + if (node->cnt == 1) { + return ans; + } + } + return w.size(); + } + +private: + array children; + int cnt; +}; + +class Solution { +public: + vector wordsAbbreviation(vector& words) { + map, Trie*> tries; + for (const auto& w : words) { + pair key = {static_cast(w.size()), w.back() - 'a'}; + if (tries.find(key) == tries.end()) { + tries[key] = new Trie(); + } + tries[key]->insert(w); + } + + vector ans; + for (const auto& w : words) { + int m = w.size(); + pair key = {m, w.back() - 'a'}; + int cnt = tries[key]->search(w); + ans.push_back((cnt + 2 >= m) ? w : w.substr(0, cnt) + to_string(m - cnt - 1) + w.back()); + } + + return ans; + } +}; +``` + ### **Go** ```go type Trie struct { children [26]*Trie - v [26]int + cnt int } -func newTrie() *Trie { - return &Trie{} -} -func (this *Trie) insert(w string) { - node := this - t := w[len(w)-1] - 'a' +func (t *Trie) insert(w string) { + node := t for _, c := range w { - c -= 'a' - if node.children[c] == nil { - node.children[c] = newTrie() + idx := c - 'a' + if node.children[idx] == nil { + node.children[idx] = &Trie{} } - node = node.children[c] - node.v[t]++ + node = node.children[idx] + node.cnt++ } } -func (this *Trie) search(w string) string { - node := this - t := w[len(w)-1] - 'a' - res := &strings.Builder{} - for _, c := range w[:len(w)-1] { - res.WriteRune(c) - c -= 'a' - node = node.children[c] - if node.v[t] == 1 { - break + +func (t *Trie) search(w string) int { + node := t + ans := 0 + for _, c := range w { + ans++ + idx := c - 'a' + node = node.children[idx] + if node.cnt == 1 { + return ans } } - n := len(w) - res.Len() - 1 - if n > 0 { - res.WriteString(strconv.Itoa(n)) - } - res.WriteByte(w[len(w)-1]) - if res.Len() < len(w) { - return res.String() - } - return w + return len(w) } -func wordsAbbreviation(words []string) []string { - trees := map[int]*Trie{} +func wordsAbbreviation(words []string) (ans []string) { + tries := make(map[[2]int]*Trie) for _, w := range words { - if _, ok := trees[len(w)]; !ok { - trees[len(w)] = newTrie() + key := [2]int{len(w), int(w[len(w)-1] - 'a')} + _, exists := tries[key] + if !exists { + tries[key] = &Trie{} } + tries[key].insert(w) } + for _, w := range words { - trees[len(w)].insert(w) - } - ans := []string{} - for _, w := range words { - ans = append(ans, trees[len(w)].search(w)) + m := len(w) + key := [2]int{m, int(w[m-1] - 'a')} + cnt := tries[key].search(w) + if cnt+2 >= m { + ans = append(ans, w) + } else { + abbr := w[:cnt] + fmt.Sprintf("%d", m-cnt-1) + w[m-1:] + ans = append(ans, abbr) + } } - return ans + return +} +``` + +### **TypeScript** + +```ts +class Trie { + private children: Trie[] = Array(26); + private cnt: number = 0; + + insert(w: string): void { + let node: Trie = this; + for (const c of w) { + const idx: number = c.charCodeAt(0) - 'a'.charCodeAt(0); + if (!node.children[idx]) { + node.children[idx] = new Trie(); + } + node = node.children[idx]; + node.cnt++; + } + } + + search(w: string): number { + let node: Trie = this; + let ans: number = 0; + for (const c of w) { + ans++; + const idx: number = c.charCodeAt(0) - 'a'.charCodeAt(0); + node = node.children[idx]; + if (node.cnt === 1) { + return ans; + } + } + return w.length; + } +} + +function wordsAbbreviation(words: string[]): string[] { + const tries: Map = new Map(); + for (const w of words) { + const key: string = `${w.length}-${w.charCodeAt(w.length - 1) - 'a'.charCodeAt(0)}`; + if (!tries.get(key)) { + tries.set(key, new Trie()); + } + tries.get(key)!.insert(w); + } + + const ans: string[] = []; + for (const w of words) { + const m: number = w.length; + const key: string = `${m}-${w.charCodeAt(m - 1) - 'a'.charCodeAt(0)}`; + const cnt: number = tries.get(key)!.search(w); + ans.push(cnt + 2 >= m ? w : w.substring(0, cnt) + (m - cnt - 1) + w.substring(m - 1)); + } + + return ans; } ``` diff --git a/solution/0500-0599/0527.Word Abbreviation/README_EN.md b/solution/0500-0599/0527.Word Abbreviation/README_EN.md index 121e9c2e2511e..d5632c1e4e8d9 100644 --- a/solution/0500-0599/0527.Word Abbreviation/README_EN.md +++ b/solution/0500-0599/0527.Word Abbreviation/README_EN.md @@ -43,215 +43,304 @@ ## Solutions - +**Solution 1: Grouped Trie** -### **Python3** +We notice that if two words have the same abbreviation, their first and last letters must be the same, and their lengths must be the same. Therefore, we can group all words by length and last letter, and use a trie to store the information of each group of words. -```python -class Trie: - def __init__(self): - self.children = [None] * 26 - self.v = defaultdict(int) +The structure of each node in the trie is as follows: - def insert(self, w): - node = self - for c in w: - idx = ord(c) - ord('a') - if node.children[idx] is None: - node.children[idx] = Trie() - node = node.children[idx] - node.v[(w[-1], len(w))] += 1 +- `children`: An array of length $26$, representing all child nodes of this node. +- `cnt`: The number of words passing through this node. - def search(self, w): - node = self - res = [] - for c in w[:-1]: - idx = ord(c) - ord('a') - node = node.children[idx] - res.append(c) - if node.v[(w[-1], len(w))] == 1: - break - n = len(w) - len(res) - 1 - if n: - res.append(str(n)) - res.append(w[-1]) - t = ''.join(res) - return t if len(t) < len(w) else w +For each word, we insert it into the trie and record the `cnt` value of each node. +When querying, we start from the root node. For the current letter, if the `cnt` value of its corresponding child node is $1$, then we have found the unique abbreviation, and we return the length of the current prefix. Otherwise, we continue to traverse downwards. After the traversal, if we have not found a unique abbreviation, then we return the length of the original word. After getting the prefix lengths of all words, we check whether the abbreviation of the word is shorter than the original word. If it is, then we add it to the answer, otherwise we add the original word to the answer. -class Solution: - def wordsAbbreviation(self, words: List[str]) -> List[str]: - trie = Trie() - for w in words: - trie.insert(w) - return [trie.search(w) for w in words] -``` +The time complexity is $O(L)$, and the space complexity is $O(L)$. Here, $L$ is the sum of the lengths of all words. + + + +### **Python3** ```python class Trie: + __slots__ = ["children", "cnt"] + def __init__(self): self.children = [None] * 26 - self.v = Counter() + self.cnt = 0 - def insert(self, w): + def insert(self, w: str): node = self for c in w: - idx = ord(c) - ord('a') - if node.children[idx] is None: + idx = ord(c) - ord("a") + if not node.children[idx]: node.children[idx] = Trie() node = node.children[idx] - node.v[w[-1]] += 1 + node.cnt += 1 - def search(self, w): + def search(self, w: str) -> int: node = self - res = [] - for c in w[:-1]: - idx = ord(c) - ord('a') + cnt = 0 + for c in w: + cnt += 1 + idx = ord(c) - ord("a") node = node.children[idx] - res.append(c) - if node.v[w[-1]] == 1: - break - n = len(w) - len(res) - 1 - if n: - res.append(str(n)) - res.append(w[-1]) - t = ''.join(res) - return t if len(t) < len(w) else w + if node.cnt == 1: + return cnt + return len(w) class Solution: def wordsAbbreviation(self, words: List[str]) -> List[str]: - trees = {} + tries = {} for w in words: - if len(w) not in trees: - trees[len(w)] = Trie() + m = len(w) + if (m, w[-1]) not in tries: + tries[(m, w[-1])] = Trie() + tries[(m, w[-1])].insert(w) + ans = [] for w in words: - trees[len(w)].insert(w) - return [trees[len(w)].search(w) for w in words] + cnt = tries[(len(w), w[-1])].search(w) + ans.append( + w if cnt + 2 >= len(w) else w[:cnt] + str(len(w) - cnt - 1) + w[-1] + ) + return ans ``` ### **Java** ```java class Trie { - Trie[] children = new Trie[26]; - int[] v = new int[26]; + private final Trie[] children = new Trie[26]; + private int cnt; - void insert(String w) { + public void insert(String w) { Trie node = this; - int t = w.charAt(w.length() - 1) - 'a'; for (char c : w.toCharArray()) { - c -= 'a'; - if (node.children[c] == null) { - node.children[c] = new Trie(); + int idx = c - 'a'; + if (node.children[idx] == null) { + node.children[idx] = new Trie(); } - node = node.children[c]; - node.v[t]++; + node = node.children[idx]; + ++node.cnt; } } - String search(String w) { + public int search(String w) { Trie node = this; - StringBuilder res = new StringBuilder(); - int t = w.charAt(w.length() - 1) - 'a'; - for (int i = 0; i < w.length() - 1; ++i) { - char c = w.charAt(i); - node = node.children[c - 'a']; - res.append(c); - if (node.v[t] == 1) { - break; + int ans = 0; + for (char c : w.toCharArray()) { + ++ans; + int idx = c - 'a'; + node = node.children[idx]; + if (node.cnt == 1) { + return ans; } } - int n = w.length() - res.length() - 1; - if (n > 0) { - res.append(n); - } - res.append(w.charAt(w.length() - 1)); - return res.length() < w.length() ? res.toString() : w; + return w.length(); } } class Solution { public List wordsAbbreviation(List words) { - Map trees = new HashMap<>(); - for (String w : words) { - if (!trees.containsKey(w.length())) { - trees.put(w.length(), new Trie()); - } - } - for (String w : words) { - trees.get(w.length()).insert(w); + Map, Trie> tries = new HashMap<>(); + for (var w : words) { + var key = List.of(w.length(), w.charAt(w.length() - 1) - 'a'); + tries.putIfAbsent(key, new Trie()); + tries.get(key).insert(w); } List ans = new ArrayList<>(); - for (String w : words) { - ans.add(trees.get(w.length()).search(w)); + for (var w : words) { + int m = w.length(); + var key = List.of(m, w.charAt(m - 1) - 'a'); + int cnt = tries.get(key).search(w); + ans.add(cnt + 2 >= m ? w : w.substring(0, cnt) + (m - cnt - 1) + w.substring(m - 1)); } return ans; } } ``` +### **C++** + +```cpp +class Trie { +public: + Trie() + : cnt(0) { + fill(children.begin(), children.end(), nullptr); + } + + void insert(const string& w) { + Trie* node = this; + for (char c : w) { + int idx = c - 'a'; + if (node->children[idx] == nullptr) { + node->children[idx] = new Trie(); + } + node = node->children[idx]; + ++node->cnt; + } + } + + int search(const string& w) { + Trie* node = this; + int ans = 0; + for (char c : w) { + ++ans; + int idx = c - 'a'; + node = node->children[idx]; + if (node->cnt == 1) { + return ans; + } + } + return w.size(); + } + +private: + array children; + int cnt; +}; + +class Solution { +public: + vector wordsAbbreviation(vector& words) { + map, Trie*> tries; + for (const auto& w : words) { + pair key = {static_cast(w.size()), w.back() - 'a'}; + if (tries.find(key) == tries.end()) { + tries[key] = new Trie(); + } + tries[key]->insert(w); + } + + vector ans; + for (const auto& w : words) { + int m = w.size(); + pair key = {m, w.back() - 'a'}; + int cnt = tries[key]->search(w); + ans.push_back((cnt + 2 >= m) ? w : w.substr(0, cnt) + to_string(m - cnt - 1) + w.back()); + } + + return ans; + } +}; +``` + ### **Go** ```go type Trie struct { children [26]*Trie - v [26]int + cnt int } -func newTrie() *Trie { - return &Trie{} -} -func (this *Trie) insert(w string) { - node := this - t := w[len(w)-1] - 'a' +func (t *Trie) insert(w string) { + node := t for _, c := range w { - c -= 'a' - if node.children[c] == nil { - node.children[c] = newTrie() + idx := c - 'a' + if node.children[idx] == nil { + node.children[idx] = &Trie{} } - node = node.children[c] - node.v[t]++ + node = node.children[idx] + node.cnt++ } } -func (this *Trie) search(w string) string { - node := this - t := w[len(w)-1] - 'a' - res := &strings.Builder{} - for _, c := range w[:len(w)-1] { - res.WriteRune(c) - c -= 'a' - node = node.children[c] - if node.v[t] == 1 { - break + +func (t *Trie) search(w string) int { + node := t + ans := 0 + for _, c := range w { + ans++ + idx := c - 'a' + node = node.children[idx] + if node.cnt == 1 { + return ans } } - n := len(w) - res.Len() - 1 - if n > 0 { - res.WriteString(strconv.Itoa(n)) - } - res.WriteByte(w[len(w)-1]) - if res.Len() < len(w) { - return res.String() - } - return w + return len(w) } -func wordsAbbreviation(words []string) []string { - trees := map[int]*Trie{} +func wordsAbbreviation(words []string) (ans []string) { + tries := make(map[[2]int]*Trie) for _, w := range words { - if _, ok := trees[len(w)]; !ok { - trees[len(w)] = newTrie() + key := [2]int{len(w), int(w[len(w)-1] - 'a')} + _, exists := tries[key] + if !exists { + tries[key] = &Trie{} } + tries[key].insert(w) } + for _, w := range words { - trees[len(w)].insert(w) - } - ans := []string{} - for _, w := range words { - ans = append(ans, trees[len(w)].search(w)) + m := len(w) + key := [2]int{m, int(w[m-1] - 'a')} + cnt := tries[key].search(w) + if cnt+2 >= m { + ans = append(ans, w) + } else { + abbr := w[:cnt] + fmt.Sprintf("%d", m-cnt-1) + w[m-1:] + ans = append(ans, abbr) + } } - return ans + return +} +``` + +### **TypeScript** + +```ts +class Trie { + private children: Trie[] = Array(26); + private cnt: number = 0; + + insert(w: string): void { + let node: Trie = this; + for (const c of w) { + const idx: number = c.charCodeAt(0) - 'a'.charCodeAt(0); + if (!node.children[idx]) { + node.children[idx] = new Trie(); + } + node = node.children[idx]; + node.cnt++; + } + } + + search(w: string): number { + let node: Trie = this; + let ans: number = 0; + for (const c of w) { + ans++; + const idx: number = c.charCodeAt(0) - 'a'.charCodeAt(0); + node = node.children[idx]; + if (node.cnt === 1) { + return ans; + } + } + return w.length; + } +} + +function wordsAbbreviation(words: string[]): string[] { + const tries: Map = new Map(); + for (const w of words) { + const key: string = `${w.length}-${w.charCodeAt(w.length - 1) - 'a'.charCodeAt(0)}`; + if (!tries.get(key)) { + tries.set(key, new Trie()); + } + tries.get(key)!.insert(w); + } + + const ans: string[] = []; + for (const w of words) { + const m: number = w.length; + const key: string = `${m}-${w.charCodeAt(m - 1) - 'a'.charCodeAt(0)}`; + const cnt: number = tries.get(key)!.search(w); + ans.push(cnt + 2 >= m ? w : w.substring(0, cnt) + (m - cnt - 1) + w.substring(m - 1)); + } + + return ans; } ``` diff --git a/solution/0500-0599/0527.Word Abbreviation/Solution.cpp b/solution/0500-0599/0527.Word Abbreviation/Solution.cpp new file mode 100644 index 0000000000000..3bbae74b544ab --- /dev/null +++ b/solution/0500-0599/0527.Word Abbreviation/Solution.cpp @@ -0,0 +1,61 @@ +class Trie { +public: + Trie() + : cnt(0) { + fill(children.begin(), children.end(), nullptr); + } + + void insert(const string& w) { + Trie* node = this; + for (char c : w) { + int idx = c - 'a'; + if (node->children[idx] == nullptr) { + node->children[idx] = new Trie(); + } + node = node->children[idx]; + ++node->cnt; + } + } + + int search(const string& w) { + Trie* node = this; + int ans = 0; + for (char c : w) { + ++ans; + int idx = c - 'a'; + node = node->children[idx]; + if (node->cnt == 1) { + return ans; + } + } + return w.size(); + } + +private: + array children; + int cnt; +}; + +class Solution { +public: + vector wordsAbbreviation(vector& words) { + map, Trie*> tries; + for (const auto& w : words) { + pair key = {static_cast(w.size()), w.back() - 'a'}; + if (tries.find(key) == tries.end()) { + tries[key] = new Trie(); + } + tries[key]->insert(w); + } + + vector ans; + for (const auto& w : words) { + int m = w.size(); + pair key = {m, w.back() - 'a'}; + int cnt = tries[key]->search(w); + ans.push_back((cnt + 2 >= m) ? w : w.substr(0, cnt) + to_string(m - cnt - 1) + w.back()); + } + + return ans; + } +}; \ No newline at end of file diff --git a/solution/0500-0599/0527.Word Abbreviation/Solution.go b/solution/0500-0599/0527.Word Abbreviation/Solution.go index c53314735feb1..818237dca164f 100644 --- a/solution/0500-0599/0527.Word Abbreviation/Solution.go +++ b/solution/0500-0599/0527.Word Abbreviation/Solution.go @@ -1,59 +1,55 @@ type Trie struct { children [26]*Trie - v [26]int + cnt int } -func newTrie() *Trie { - return &Trie{} -} -func (this *Trie) insert(w string) { - node := this - t := w[len(w)-1] - 'a' +func (t *Trie) insert(w string) { + node := t for _, c := range w { - c -= 'a' - if node.children[c] == nil { - node.children[c] = newTrie() + idx := c - 'a' + if node.children[idx] == nil { + node.children[idx] = &Trie{} } - node = node.children[c] - node.v[t]++ + node = node.children[idx] + node.cnt++ } } -func (this *Trie) search(w string) string { - node := this - t := w[len(w)-1] - 'a' - res := &strings.Builder{} - for _, c := range w[:len(w)-1] { - res.WriteRune(c) - c -= 'a' - node = node.children[c] - if node.v[t] == 1 { - break + +func (t *Trie) search(w string) int { + node := t + ans := 0 + for _, c := range w { + ans++ + idx := c - 'a' + node = node.children[idx] + if node.cnt == 1 { + return ans } } - n := len(w) - res.Len() - 1 - if n > 0 { - res.WriteString(strconv.Itoa(n)) - } - res.WriteByte(w[len(w)-1]) - if res.Len() < len(w) { - return res.String() - } - return w + return len(w) } -func wordsAbbreviation(words []string) []string { - trees := map[int]*Trie{} +func wordsAbbreviation(words []string) (ans []string) { + tries := make(map[[2]int]*Trie) for _, w := range words { - if _, ok := trees[len(w)]; !ok { - trees[len(w)] = newTrie() + key := [2]int{len(w), int(w[len(w)-1] - 'a')} + _, exists := tries[key] + if !exists { + tries[key] = &Trie{} } + tries[key].insert(w) } + for _, w := range words { - trees[len(w)].insert(w) - } - ans := []string{} - for _, w := range words { - ans = append(ans, trees[len(w)].search(w)) + m := len(w) + key := [2]int{m, int(w[m-1] - 'a')} + cnt := tries[key].search(w) + if cnt+2 >= m { + ans = append(ans, w) + } else { + abbr := w[:cnt] + fmt.Sprintf("%d", m-cnt-1) + w[m-1:] + ans = append(ans, abbr) + } } - return ans + return } \ No newline at end of file diff --git a/solution/0500-0599/0527.Word Abbreviation/Solution.java b/solution/0500-0599/0527.Word Abbreviation/Solution.java index 9455a3f6fcf54..fbf65c878e2c2 100644 --- a/solution/0500-0599/0527.Word Abbreviation/Solution.java +++ b/solution/0500-0599/0527.Word Abbreviation/Solution.java @@ -1,56 +1,49 @@ -class Trie { - Trie[] children = new Trie[26]; - int[] v = new int[26]; - - void insert(String w) { - Trie node = this; - int t = w.charAt(w.length() - 1) - 'a'; - for (char c : w.toCharArray()) { - c -= 'a'; - if (node.children[c] == null) { - node.children[c] = new Trie(); - } - node = node.children[c]; - node.v[t]++; - } - } - - String search(String w) { - Trie node = this; - StringBuilder res = new StringBuilder(); - int t = w.charAt(w.length() - 1) - 'a'; - for (int i = 0; i < w.length() - 1; ++i) { - char c = w.charAt(i); - node = node.children[c - 'a']; - res.append(c); - if (node.v[t] == 1) { - break; - } - } - int n = w.length() - res.length() - 1; - if (n > 0) { - res.append(n); - } - res.append(w.charAt(w.length() - 1)); - return res.length() < w.length() ? res.toString() : w; - } -} - -class Solution { - public List wordsAbbreviation(List words) { - Map trees = new HashMap<>(); - for (String w : words) { - if (!trees.containsKey(w.length())) { - trees.put(w.length(), new Trie()); - } - } - for (String w : words) { - trees.get(w.length()).insert(w); - } - List ans = new ArrayList<>(); - for (String w : words) { - ans.add(trees.get(w.length()).search(w)); - } - return ans; - } +class Trie { + private final Trie[] children = new Trie[26]; + private int cnt; + + public void insert(String w) { + Trie node = this; + for (char c : w.toCharArray()) { + int idx = c - 'a'; + if (node.children[idx] == null) { + node.children[idx] = new Trie(); + } + node = node.children[idx]; + ++node.cnt; + } + } + + public int search(String w) { + Trie node = this; + int ans = 0; + for (char c : w.toCharArray()) { + ++ans; + int idx = c - 'a'; + node = node.children[idx]; + if (node.cnt == 1) { + return ans; + } + } + return w.length(); + } +} + +class Solution { + public List wordsAbbreviation(List words) { + Map, Trie> tries = new HashMap<>(); + for (var w : words) { + var key = List.of(w.length(), w.charAt(w.length() - 1) - 'a'); + tries.putIfAbsent(key, new Trie()); + tries.get(key).insert(w); + } + List ans = new ArrayList<>(); + for (var w : words) { + int m = w.length(); + var key = List.of(m, w.charAt(m - 1) - 'a'); + int cnt = tries.get(key).search(w); + ans.add(cnt + 2 >= m ? w : w.substring(0, cnt) + (m - cnt - 1) + w.substring(m - 1)); + } + return ans; + } } \ No newline at end of file diff --git a/solution/0500-0599/0527.Word Abbreviation/Solution.py b/solution/0500-0599/0527.Word Abbreviation/Solution.py index 04e213ab4e432..380679cb7aee7 100644 --- a/solution/0500-0599/0527.Word Abbreviation/Solution.py +++ b/solution/0500-0599/0527.Word Abbreviation/Solution.py @@ -1,37 +1,43 @@ -class Trie: - def __init__(self): - self.children = [None] * 26 - self.v = defaultdict(int) - - def insert(self, w): - node = self - for c in w: - idx = ord(c) - ord('a') - if node.children[idx] is None: - node.children[idx] = Trie() - node = node.children[idx] - node.v[(w[-1], len(w))] += 1 - - def search(self, w): - node = self - res = [] - for c in w[:-1]: - idx = ord(c) - ord('a') - node = node.children[idx] - res.append(c) - if node.v[(w[-1], len(w))] == 1: - break - n = len(w) - len(res) - 1 - if n: - res.append(str(n)) - res.append(w[-1]) - t = ''.join(res) - return t if len(t) < len(w) else w - - -class Solution: - def wordsAbbreviation(self, words: List[str]) -> List[str]: - trie = Trie() - for w in words: - trie.insert(w) - return [trie.search(w) for w in words] +class Trie: + __slots__ = ["children", "cnt"] + + def __init__(self): + self.children = [None] * 26 + self.cnt = 0 + + def insert(self, w: str): + node = self + for c in w: + idx = ord(c) - ord("a") + if not node.children[idx]: + node.children[idx] = Trie() + node = node.children[idx] + node.cnt += 1 + + def search(self, w: str) -> int: + node = self + cnt = 0 + for c in w: + cnt += 1 + idx = ord(c) - ord("a") + node = node.children[idx] + if node.cnt == 1: + return cnt + return len(w) + + +class Solution: + def wordsAbbreviation(self, words: List[str]) -> List[str]: + tries = {} + for w in words: + m = len(w) + if (m, w[-1]) not in tries: + tries[(m, w[-1])] = Trie() + tries[(m, w[-1])].insert(w) + ans = [] + for w in words: + cnt = tries[(len(w), w[-1])].search(w) + ans.append( + w if cnt + 2 >= len(w) else w[:cnt] + str(len(w) - cnt - 1) + w[-1] + ) + return ans diff --git a/solution/0500-0599/0527.Word Abbreviation/Solution.ts b/solution/0500-0599/0527.Word Abbreviation/Solution.ts new file mode 100644 index 0000000000000..8a3d39127fd60 --- /dev/null +++ b/solution/0500-0599/0527.Word Abbreviation/Solution.ts @@ -0,0 +1,51 @@ +class Trie { + private children: Trie[] = Array(26); + private cnt: number = 0; + + insert(w: string): void { + let node: Trie = this; + for (const c of w) { + const idx: number = c.charCodeAt(0) - 'a'.charCodeAt(0); + if (!node.children[idx]) { + node.children[idx] = new Trie(); + } + node = node.children[idx]; + node.cnt++; + } + } + + search(w: string): number { + let node: Trie = this; + let ans: number = 0; + for (const c of w) { + ans++; + const idx: number = c.charCodeAt(0) - 'a'.charCodeAt(0); + node = node.children[idx]; + if (node.cnt === 1) { + return ans; + } + } + return w.length; + } +} + +function wordsAbbreviation(words: string[]): string[] { + const tries: Map = new Map(); + for (const w of words) { + const key: string = `${w.length}-${w.charCodeAt(w.length - 1) - 'a'.charCodeAt(0)}`; + if (!tries.get(key)) { + tries.set(key, new Trie()); + } + tries.get(key)!.insert(w); + } + + const ans: string[] = []; + for (const w of words) { + const m: number = w.length; + const key: string = `${m}-${w.charCodeAt(m - 1) - 'a'.charCodeAt(0)}`; + const cnt: number = tries.get(key)!.search(w); + ans.push(cnt + 2 >= m ? w : w.substring(0, cnt) + (m - cnt - 1) + w.substring(m - 1)); + } + + return ans; +}