From 671741e84059b5161ebc928b61b3f59cc332ada3 Mon Sep 17 00:00:00 2001 From: yanglbme Date: Sun, 29 Jun 2025 19:59:00 +0800 Subject: [PATCH] feat: add solutions to lc problem: No.3598 No.3598.Longest Common Prefix Between Adjacent Strings After Removals --- .../README.md | 209 +++++++++++++++++- .../README_EN.md | 209 +++++++++++++++++- .../Solution.cpp | 44 ++++ .../Solution.go | 61 +++++ .../Solution.java | 49 ++++ .../Solution.py | 31 +++ 6 files changed, 593 insertions(+), 10 deletions(-) create mode 100644 solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/Solution.cpp create mode 100644 solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/Solution.go create mode 100644 solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/Solution.java create mode 100644 solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/Solution.py diff --git a/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/README.md b/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/README.md index 3314eb2da8fbe..ef4cfa775699c 100644 --- a/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/README.md +++ b/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/README.md @@ -101,32 +101,231 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3500-3599/3598.Lo -### 方法一 +### 方法一:有序集合 + +我们定义一个函数 $\textit{calc}(s, t)$,它计算字符串 $s$ 和 $t$ 的最长公共前缀的长度。我们可以使用有序集合来维护所有相邻字符串对的最长公共前缀长度。 + +定义一个函数 $\textit{add}(i, j)$,它将下标 $i$ 和 $j$ 处的字符串对的最长公共前缀长度添加到有序集合中。定义一个函数 $\textit{remove}(i, j)$,它从有序集合中移除下标 $i$ 和 $j$ 处的字符串对的最长公共前缀长度。 + +我们首先计算所有相邻字符串对的最长公共前缀长度,并将其存储在有序集合中。然后,我们遍历每个下标 $i$,执行以下操作: + +1. 移除下标 $i$ 和 $i + 1$ 处的字符串对的最长公共前缀长度。 +2. 移除下标 $i - 1$ 和 $i$ 处的字符串对的最长公共前缀长度。 +3. 添加下标 $i - 1$ 和 $i + 1$ 处的字符串对的最长公共前缀长度。 +4. 将当前有序集合中的最大值(如果存在且大于 0)添加到答案中。 +5. 移除下标 $i - 1$ 和 $i + 1$ 处的字符串对的最长公共前缀长度。 +6. 添加下标 $i - 1$ 和 $i$ 处的字符串对的最长公共前缀长度。 +7. 添加下标 $i$ 和 $i + 1$ 处的字符串对的最长公共前缀长度。 + +这样,我们可以在每次移除一个字符串后,快速计算出相邻字符串对的最长公共前缀长度。 + +时间复杂度 $O(L + n \times \log n)$,空间复杂度 $O(n)$,其中 $L$ 是所有字符串的总长度,而 $n$ 是字符串的数量。 #### Python3 ```python - +class Solution: + def longestCommonPrefix(self, words: List[str]) -> List[int]: + @cache + def calc(s: str, t: str) -> int: + k = 0 + for a, b in zip(s, t): + if a != b: + break + k += 1 + return k + + def add(i: int, j: int): + if 0 <= i < n and 0 <= j < n: + sl.add(calc(words[i], words[j])) + + def remove(i: int, j: int): + if 0 <= i < n and 0 <= j < n: + sl.remove(calc(words[i], words[j])) + + n = len(words) + sl = SortedList(calc(a, b) for a, b in pairwise(words)) + ans = [] + for i in range(n): + remove(i, i + 1) + remove(i - 1, i) + add(i - 1, i + 1) + ans.append(sl[-1] if sl and sl[-1] > 0 else 0) + remove(i - 1, i + 1) + add(i - 1, i) + add(i, i + 1) + return ans ``` #### Java ```java - +class Solution { + private final TreeMap tm = new TreeMap<>(); + private String[] words; + private int n; + + public int[] longestCommonPrefix(String[] words) { + n = words.length; + this.words = words; + for (int i = 0; i + 1 < n; ++i) { + tm.merge(calc(words[i], words[i + 1]), 1, Integer::sum); + } + int[] ans = new int[n]; + for (int i = 0; i < n; ++i) { + remove(i, i + 1); + remove(i - 1, i); + add(i - 1, i + 1); + ans[i] = !tm.isEmpty() && tm.lastKey() > 0 ? tm.lastKey() : 0; + remove(i - 1, i + 1); + add(i - 1, i); + add(i, i + 1); + } + return ans; + } + + private void add(int i, int j) { + if (i >= 0 && i < n && j >= 0 && j < n) { + tm.merge(calc(words[i], words[j]), 1, Integer::sum); + } + } + + private void remove(int i, int j) { + if (i >= 0 && i < n && j >= 0 && j < n) { + int x = calc(words[i], words[j]); + if (tm.merge(x, -1, Integer::sum) == 0) { + tm.remove(x); + } + } + } + + private int calc(String s, String t) { + int m = Math.min(s.length(), t.length()); + for (int k = 0; k < m; ++k) { + if (s.charAt(k) != t.charAt(k)) { + return k; + } + } + return m; + } +} ``` #### C++ ```cpp - +class Solution { +public: + vector longestCommonPrefix(vector& words) { + multiset ms; + int n = words.size(); + auto calc = [&](const string& s, const string& t) { + int m = min(s.size(), t.size()); + for (int k = 0; k < m; ++k) { + if (s[k] != t[k]) { + return k; + } + } + return m; + }; + for (int i = 0; i + 1 < n; ++i) { + ms.insert(calc(words[i], words[i + 1])); + } + vector ans(n); + auto add = [&](int i, int j) { + if (i >= 0 && i < n && j >= 0 && j < n) { + ms.insert(calc(words[i], words[j])); + } + }; + auto remove = [&](int i, int j) { + if (i >= 0 && i < n && j >= 0 && j < n) { + int x = calc(words[i], words[j]); + auto it = ms.find(x); + if (it != ms.end()) { + ms.erase(it); + } + } + }; + for (int i = 0; i < n; ++i) { + remove(i, i + 1); + remove(i - 1, i); + add(i - 1, i + 1); + ans[i] = ms.empty() ? 0 : *ms.rbegin(); + remove(i - 1, i + 1); + add(i - 1, i); + add(i, i + 1); + } + return ans; + } +}; ``` #### Go ```go - +func longestCommonPrefix(words []string) []int { + n := len(words) + tm := treemap.NewWithIntComparator() + + calc := func(s, t string) int { + m := min(len(s), len(t)) + for k := 0; k < m; k++ { + if s[k] != t[k] { + return k + } + } + return m + } + + add := func(i, j int) { + if i >= 0 && i < n && j >= 0 && j < n { + x := calc(words[i], words[j]) + if v, ok := tm.Get(x); ok { + tm.Put(x, v.(int)+1) + } else { + tm.Put(x, 1) + } + } + } + + remove := func(i, j int) { + if i >= 0 && i < n && j >= 0 && j < n { + x := calc(words[i], words[j]) + if v, ok := tm.Get(x); ok { + if v.(int) == 1 { + tm.Remove(x) + } else { + tm.Put(x, v.(int)-1) + } + } + } + } + + for i := 0; i+1 < n; i++ { + add(i, i+1) + } + + ans := make([]int, n) + for i := 0; i < n; i++ { + remove(i, i+1) + remove(i-1, i) + add(i-1, i+1) + + if !tm.Empty() { + if maxKey, _ := tm.Max(); maxKey.(int) > 0 { + ans[i] = maxKey.(int) + } + } + + remove(i-1, i+1) + add(i-1, i) + add(i, i+1) + } + + return ans +} ``` diff --git a/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/README_EN.md b/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/README_EN.md index aa645c7c29d09..9a6caade7b9b6 100644 --- a/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/README_EN.md +++ b/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/README_EN.md @@ -97,32 +97,231 @@ A prefix of a string is a substring that starts from the beginning of the string -### Solution 1 +### Solution 1: Ordered Set + +We define a function $\textit{calc}(s, t)$, which calculates the length of the longest common prefix between strings $s$ and $t$. We can use an ordered set to maintain the longest common prefix lengths of all adjacent string pairs. + +Define a function $\textit{add}(i, j)$, which adds the longest common prefix length of the string pair at indices $i$ and $j$ to the ordered set. Define a function $\textit{remove}(i, j)$, which removes the longest common prefix length of the string pair at indices $i$ and $j$ from the ordered set. + +First, we compute the longest common prefix lengths for all adjacent string pairs and store them in the ordered set. Then, for each index $i$, we perform the following steps: + +1. Remove the longest common prefix length of the string pair at indices $i$ and $i + 1$. +2. Remove the longest common prefix length of the string pair at indices $i - 1$ and $i$. +3. Add the longest common prefix length of the string pair at indices $i - 1$ and $i + 1$. +4. Add the current maximum value in the ordered set (if it exists and is greater than 0) to the answer. +5. Remove the longest common prefix length of the string pair at indices $i - 1$ and $i + 1$. +6. Add the longest common prefix length of the string pair at indices $i - 1$ and $i$. +7. Add the longest common prefix length of the string pair at indices $i$ and $i + 1$. + +In this way, after removing each string, we can quickly compute the longest common prefix length between adjacent string pairs. + +The time complexity is $O(L + n \times \log n)$, and the space complexity is $O(n)$, where $L$ is the total length of all strings and $n$ is the number of strings. #### Python3 ```python - +class Solution: + def longestCommonPrefix(self, words: List[str]) -> List[int]: + @cache + def calc(s: str, t: str) -> int: + k = 0 + for a, b in zip(s, t): + if a != b: + break + k += 1 + return k + + def add(i: int, j: int): + if 0 <= i < n and 0 <= j < n: + sl.add(calc(words[i], words[j])) + + def remove(i: int, j: int): + if 0 <= i < n and 0 <= j < n: + sl.remove(calc(words[i], words[j])) + + n = len(words) + sl = SortedList(calc(a, b) for a, b in pairwise(words)) + ans = [] + for i in range(n): + remove(i, i + 1) + remove(i - 1, i) + add(i - 1, i + 1) + ans.append(sl[-1] if sl and sl[-1] > 0 else 0) + remove(i - 1, i + 1) + add(i - 1, i) + add(i, i + 1) + return ans ``` #### Java ```java - +class Solution { + private final TreeMap tm = new TreeMap<>(); + private String[] words; + private int n; + + public int[] longestCommonPrefix(String[] words) { + n = words.length; + this.words = words; + for (int i = 0; i + 1 < n; ++i) { + tm.merge(calc(words[i], words[i + 1]), 1, Integer::sum); + } + int[] ans = new int[n]; + for (int i = 0; i < n; ++i) { + remove(i, i + 1); + remove(i - 1, i); + add(i - 1, i + 1); + ans[i] = !tm.isEmpty() && tm.lastKey() > 0 ? tm.lastKey() : 0; + remove(i - 1, i + 1); + add(i - 1, i); + add(i, i + 1); + } + return ans; + } + + private void add(int i, int j) { + if (i >= 0 && i < n && j >= 0 && j < n) { + tm.merge(calc(words[i], words[j]), 1, Integer::sum); + } + } + + private void remove(int i, int j) { + if (i >= 0 && i < n && j >= 0 && j < n) { + int x = calc(words[i], words[j]); + if (tm.merge(x, -1, Integer::sum) == 0) { + tm.remove(x); + } + } + } + + private int calc(String s, String t) { + int m = Math.min(s.length(), t.length()); + for (int k = 0; k < m; ++k) { + if (s.charAt(k) != t.charAt(k)) { + return k; + } + } + return m; + } +} ``` #### C++ ```cpp - +class Solution { +public: + vector longestCommonPrefix(vector& words) { + multiset ms; + int n = words.size(); + auto calc = [&](const string& s, const string& t) { + int m = min(s.size(), t.size()); + for (int k = 0; k < m; ++k) { + if (s[k] != t[k]) { + return k; + } + } + return m; + }; + for (int i = 0; i + 1 < n; ++i) { + ms.insert(calc(words[i], words[i + 1])); + } + vector ans(n); + auto add = [&](int i, int j) { + if (i >= 0 && i < n && j >= 0 && j < n) { + ms.insert(calc(words[i], words[j])); + } + }; + auto remove = [&](int i, int j) { + if (i >= 0 && i < n && j >= 0 && j < n) { + int x = calc(words[i], words[j]); + auto it = ms.find(x); + if (it != ms.end()) { + ms.erase(it); + } + } + }; + for (int i = 0; i < n; ++i) { + remove(i, i + 1); + remove(i - 1, i); + add(i - 1, i + 1); + ans[i] = ms.empty() ? 0 : *ms.rbegin(); + remove(i - 1, i + 1); + add(i - 1, i); + add(i, i + 1); + } + return ans; + } +}; ``` #### Go ```go - +func longestCommonPrefix(words []string) []int { + n := len(words) + tm := treemap.NewWithIntComparator() + + calc := func(s, t string) int { + m := min(len(s), len(t)) + for k := 0; k < m; k++ { + if s[k] != t[k] { + return k + } + } + return m + } + + add := func(i, j int) { + if i >= 0 && i < n && j >= 0 && j < n { + x := calc(words[i], words[j]) + if v, ok := tm.Get(x); ok { + tm.Put(x, v.(int)+1) + } else { + tm.Put(x, 1) + } + } + } + + remove := func(i, j int) { + if i >= 0 && i < n && j >= 0 && j < n { + x := calc(words[i], words[j]) + if v, ok := tm.Get(x); ok { + if v.(int) == 1 { + tm.Remove(x) + } else { + tm.Put(x, v.(int)-1) + } + } + } + } + + for i := 0; i+1 < n; i++ { + add(i, i+1) + } + + ans := make([]int, n) + for i := 0; i < n; i++ { + remove(i, i+1) + remove(i-1, i) + add(i-1, i+1) + + if !tm.Empty() { + if maxKey, _ := tm.Max(); maxKey.(int) > 0 { + ans[i] = maxKey.(int) + } + } + + remove(i-1, i+1) + add(i-1, i) + add(i, i+1) + } + + return ans +} ``` diff --git a/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/Solution.cpp b/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/Solution.cpp new file mode 100644 index 0000000000000..596f9cc2744b7 --- /dev/null +++ b/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/Solution.cpp @@ -0,0 +1,44 @@ +class Solution { +public: + vector longestCommonPrefix(vector& words) { + multiset ms; + int n = words.size(); + auto calc = [&](const string& s, const string& t) { + int m = min(s.size(), t.size()); + for (int k = 0; k < m; ++k) { + if (s[k] != t[k]) { + return k; + } + } + return m; + }; + for (int i = 0; i + 1 < n; ++i) { + ms.insert(calc(words[i], words[i + 1])); + } + vector ans(n); + auto add = [&](int i, int j) { + if (i >= 0 && i < n && j >= 0 && j < n) { + ms.insert(calc(words[i], words[j])); + } + }; + auto remove = [&](int i, int j) { + if (i >= 0 && i < n && j >= 0 && j < n) { + int x = calc(words[i], words[j]); + auto it = ms.find(x); + if (it != ms.end()) { + ms.erase(it); + } + } + }; + for (int i = 0; i < n; ++i) { + remove(i, i + 1); + remove(i - 1, i); + add(i - 1, i + 1); + ans[i] = ms.empty() ? 0 : *ms.rbegin(); + remove(i - 1, i + 1); + add(i - 1, i); + add(i, i + 1); + } + return ans; + } +}; diff --git a/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/Solution.go b/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/Solution.go new file mode 100644 index 0000000000000..508babde435df --- /dev/null +++ b/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/Solution.go @@ -0,0 +1,61 @@ +func longestCommonPrefix(words []string) []int { + n := len(words) + tm := treemap.NewWithIntComparator() + + calc := func(s, t string) int { + m := min(len(s), len(t)) + for k := 0; k < m; k++ { + if s[k] != t[k] { + return k + } + } + return m + } + + add := func(i, j int) { + if i >= 0 && i < n && j >= 0 && j < n { + x := calc(words[i], words[j]) + if v, ok := tm.Get(x); ok { + tm.Put(x, v.(int)+1) + } else { + tm.Put(x, 1) + } + } + } + + remove := func(i, j int) { + if i >= 0 && i < n && j >= 0 && j < n { + x := calc(words[i], words[j]) + if v, ok := tm.Get(x); ok { + if v.(int) == 1 { + tm.Remove(x) + } else { + tm.Put(x, v.(int)-1) + } + } + } + } + + for i := 0; i+1 < n; i++ { + add(i, i+1) + } + + ans := make([]int, n) + for i := 0; i < n; i++ { + remove(i, i+1) + remove(i-1, i) + add(i-1, i+1) + + if !tm.Empty() { + if maxKey, _ := tm.Max(); maxKey.(int) > 0 { + ans[i] = maxKey.(int) + } + } + + remove(i-1, i+1) + add(i-1, i) + add(i, i+1) + } + + return ans +} \ No newline at end of file diff --git a/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/Solution.java b/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/Solution.java new file mode 100644 index 0000000000000..207ac75f57b4d --- /dev/null +++ b/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/Solution.java @@ -0,0 +1,49 @@ +class Solution { + private final TreeMap tm = new TreeMap<>(); + private String[] words; + private int n; + + public int[] longestCommonPrefix(String[] words) { + n = words.length; + this.words = words; + for (int i = 0; i + 1 < n; ++i) { + tm.merge(calc(words[i], words[i + 1]), 1, Integer::sum); + } + int[] ans = new int[n]; + for (int i = 0; i < n; ++i) { + remove(i, i + 1); + remove(i - 1, i); + add(i - 1, i + 1); + ans[i] = !tm.isEmpty() && tm.lastKey() > 0 ? tm.lastKey() : 0; + remove(i - 1, i + 1); + add(i - 1, i); + add(i, i + 1); + } + return ans; + } + + private void add(int i, int j) { + if (i >= 0 && i < n && j >= 0 && j < n) { + tm.merge(calc(words[i], words[j]), 1, Integer::sum); + } + } + + private void remove(int i, int j) { + if (i >= 0 && i < n && j >= 0 && j < n) { + int x = calc(words[i], words[j]); + if (tm.merge(x, -1, Integer::sum) == 0) { + tm.remove(x); + } + } + } + + private int calc(String s, String t) { + int m = Math.min(s.length(), t.length()); + for (int k = 0; k < m; ++k) { + if (s.charAt(k) != t.charAt(k)) { + return k; + } + } + return m; + } +} \ No newline at end of file diff --git a/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/Solution.py b/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/Solution.py new file mode 100644 index 0000000000000..1addc7fa7074d --- /dev/null +++ b/solution/3500-3599/3598.Longest Common Prefix Between Adjacent Strings After Removals/Solution.py @@ -0,0 +1,31 @@ +class Solution: + def longestCommonPrefix(self, words: List[str]) -> List[int]: + @cache + def calc(s: str, t: str) -> int: + k = 0 + for a, b in zip(s, t): + if a != b: + break + k += 1 + return k + + def add(i: int, j: int): + if 0 <= i < n and 0 <= j < n: + sl.add(calc(words[i], words[j])) + + def remove(i: int, j: int): + if 0 <= i < n and 0 <= j < n: + sl.remove(calc(words[i], words[j])) + + n = len(words) + sl = SortedList(calc(a, b) for a, b in pairwise(words)) + ans = [] + for i in range(n): + remove(i, i + 1) + remove(i - 1, i) + add(i - 1, i + 1) + ans.append(sl[-1] if sl and sl[-1] > 0 else 0) + remove(i - 1, i + 1) + add(i - 1, i) + add(i, i + 1) + return ans