diff --git a/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/README.md b/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/README.md index f199d46dc3716..5ea8a2e7b46b6 100644 --- a/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/README.md +++ b/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/README.md @@ -68,9 +68,17 @@ tags: -### 方法一:DFS +### 方法一:回溯 + 剪枝 -经典 DFS 回溯问题。 +我们定义一个哈希表 $\textit{st}$,用于存储当前已经拆分出的子字符串。然后我们使用深度优先搜索的方式,尝试将字符串 $\textit{s}$ 拆分成若干个唯一的子字符串。 + +具体地,我们设计一个函数 $\text{dfs}(i)$,表示我们正在考虑将 $\textit{s}[i:]$ 进行拆分。 + +在函数 $\text{dfs}(i)$ 中,我们首先判断如果当前已经拆分出的子字符串的数量加上剩余的字符数小于等于当前的答案,那么我们就没有必要继续拆分,直接返回。如果 $i \geq n$,那么说明我们已经完成了对整个字符串的拆分,我们更新答案为当前的子字符串数量和答案的较大值。否则,我们枚举当前子字符串的结束位置 $j$(不包括 $j$),并判断 $\textit{s}[i..j)$ 是否已经被拆分出来。如果没有被拆分出来,我们将其加入到哈希表 $\textit{st}$ 中,并继续递归地考虑拆分剩余的部分。在递归调用结束后,我们需要将 $\textit{s}[i..j)$ 从哈希表 $\textit{st}$ 中移除。 + +最后,我们返回答案。 + +时间复杂度 $O(n^2 \times 2^n)$,空间复杂度 $O(n)$。其中 $n$ 为字符串 $\textit{s}$ 的长度。 @@ -79,20 +87,22 @@ tags: ```python class Solution: def maxUniqueSplit(self, s: str) -> int: - def dfs(i, t): + def dfs(i: int): + nonlocal ans + if len(st) + len(s) - i <= ans: + return if i >= len(s): - nonlocal ans - ans = max(ans, t) + ans = max(ans, len(st)) return for j in range(i + 1, len(s) + 1): - if s[i:j] not in vis: - vis.add(s[i:j]) - dfs(j, t + 1) - vis.remove(s[i:j]) - - vis = set() - ans = 1 - dfs(0, 0) + if s[i:j] not in st: + st.add(s[i:j]) + dfs(j) + st.remove(s[i:j]) + + ans = 0 + st = set() + dfs(0) return ans ``` @@ -100,26 +110,29 @@ class Solution: ```java class Solution { - private Set vis = new HashSet<>(); - private int ans = 1; + private Set st = new HashSet<>(); + private int ans; private String s; public int maxUniqueSplit(String s) { this.s = s; - dfs(0, 0); + dfs(0); return ans; } - private void dfs(int i, int t) { + private void dfs(int i) { + if (st.size() + s.length() - i <= ans) { + return; + } if (i >= s.length()) { - ans = Math.max(ans, t); + ans = Math.max(ans, st.size()); return; } for (int j = i + 1; j <= s.length(); ++j) { - String x = s.substring(i, j); - if (vis.add(x)) { - dfs(j, t + 1); - vis.remove(x); + String t = s.substring(i, j); + if (st.add(t)) { + dfs(j); + st.remove(t); } } } @@ -131,29 +144,29 @@ class Solution { ```cpp class Solution { public: - unordered_set vis; - string s; - int ans = 1; - int maxUniqueSplit(string s) { - this->s = s; - dfs(0, 0); - return ans; - } - - void dfs(int i, int t) { - if (i >= s.size()) { - ans = max(ans, t); - return; - } - for (int j = i + 1; j <= s.size(); ++j) { - string x = s.substr(i, j - i); - if (!vis.count(x)) { - vis.insert(x); - dfs(j, t + 1); - vis.erase(x); + unordered_set st; + int n = s.size(); + int ans = 0; + auto dfs = [&](this auto&& dfs, int i) -> void { + if (st.size() + n - i <= ans) { + return; } - } + if (i >= n) { + ans = max(ans, (int) st.size()); + return; + } + for (int j = i + 1; j <= n; ++j) { + string t = s.substr(i, j - i); + if (!st.contains(t)) { + st.insert(t); + dfs(j); + st.erase(t); + } + } + }; + dfs(0); + return ans; } }; ``` @@ -161,27 +174,57 @@ public: #### Go ```go -func maxUniqueSplit(s string) int { - ans := 1 - vis := map[string]bool{} - - var dfs func(i, t int) - dfs = func(i, t int) { - if i >= len(s) { - ans = max(ans, t) +func maxUniqueSplit(s string) (ans int) { + st := map[string]bool{} + n := len(s) + var dfs func(int) + dfs = func(i int) { + if len(st)+n-i <= ans { + return + } + if i >= n { + ans = max(ans, len(st)) return } - for j := i + 1; j <= len(s); j++ { - x := s[i:j] - if !vis[x] { - vis[x] = true - dfs(j, t+1) - vis[x] = false + for j := i + 1; j <= n; j++ { + if t := s[i:j]; !st[t] { + st[t] = true + dfs(j) + delete(st, t) } } } - dfs(0, 0) - return ans + dfs(0) + return +} +``` + +#### TypeScript + +```ts +function maxUniqueSplit(s: string): number { + const n = s.length; + const st = new Set(); + let ans = 0; + const dfs = (i: number): void => { + if (st.size + n - i <= ans) { + return; + } + if (i >= n) { + ans = Math.max(ans, st.size); + return; + } + for (let j = i + 1; j <= n; ++j) { + const t = s.slice(i, j); + if (!st.has(t)) { + st.add(t); + dfs(j); + st.delete(t); + } + } + }; + dfs(0); + return ans; } ``` diff --git a/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/README_EN.md b/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/README_EN.md index a3b49ca4adb4b..07a8dbb6f42a9 100644 --- a/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/README_EN.md +++ b/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/README_EN.md @@ -69,7 +69,17 @@ tags: -### Solution 1 +### Solution 1: Backtracking + Pruning + +We define a hash table $\textit{st}$ to store the currently split substrings. Then we use a depth-first search approach to try to split the string $\textit{s}$ into several unique substrings. + +Specifically, we design a function $\text{dfs}(i)$, which means we are considering splitting $\textit{s}[i:]$. + +In the function $\text{dfs}(i)$, we first check if the number of substrings already split plus the remaining characters is less than or equal to the current answer. If so, there is no need to continue splitting, and we return directly. If $i \geq n$, it means we have completed the splitting of the entire string, and we update the answer to the maximum of the current number of substrings and the answer. Otherwise, we enumerate the end position $j$ (exclusive) of the current substring and check if $\textit{s}[i..j)$ has already been split. If not, we add it to the hash table $\textit{st}$ and continue to recursively consider splitting the remaining part. After the recursive call, we need to remove $\textit{s}[i..j)$ from the hash table $\textit{st}$. + +Finally, we return the answer. + +The time complexity is $O(n^2 \times 2^n)$, and the space complexity is $O(n)$. Here, $n$ is the length of the string $\textit{s}$. @@ -78,20 +88,22 @@ tags: ```python class Solution: def maxUniqueSplit(self, s: str) -> int: - def dfs(i, t): + def dfs(i: int): + nonlocal ans + if len(st) + len(s) - i <= ans: + return if i >= len(s): - nonlocal ans - ans = max(ans, t) + ans = max(ans, len(st)) return for j in range(i + 1, len(s) + 1): - if s[i:j] not in vis: - vis.add(s[i:j]) - dfs(j, t + 1) - vis.remove(s[i:j]) - - vis = set() - ans = 1 - dfs(0, 0) + if s[i:j] not in st: + st.add(s[i:j]) + dfs(j) + st.remove(s[i:j]) + + ans = 0 + st = set() + dfs(0) return ans ``` @@ -99,26 +111,29 @@ class Solution: ```java class Solution { - private Set vis = new HashSet<>(); - private int ans = 1; + private Set st = new HashSet<>(); + private int ans; private String s; public int maxUniqueSplit(String s) { this.s = s; - dfs(0, 0); + dfs(0); return ans; } - private void dfs(int i, int t) { + private void dfs(int i) { + if (st.size() + s.length() - i <= ans) { + return; + } if (i >= s.length()) { - ans = Math.max(ans, t); + ans = Math.max(ans, st.size()); return; } for (int j = i + 1; j <= s.length(); ++j) { - String x = s.substring(i, j); - if (vis.add(x)) { - dfs(j, t + 1); - vis.remove(x); + String t = s.substring(i, j); + if (st.add(t)) { + dfs(j); + st.remove(t); } } } @@ -130,29 +145,29 @@ class Solution { ```cpp class Solution { public: - unordered_set vis; - string s; - int ans = 1; - int maxUniqueSplit(string s) { - this->s = s; - dfs(0, 0); - return ans; - } - - void dfs(int i, int t) { - if (i >= s.size()) { - ans = max(ans, t); - return; - } - for (int j = i + 1; j <= s.size(); ++j) { - string x = s.substr(i, j - i); - if (!vis.count(x)) { - vis.insert(x); - dfs(j, t + 1); - vis.erase(x); + unordered_set st; + int n = s.size(); + int ans = 0; + auto dfs = [&](this auto&& dfs, int i) -> void { + if (st.size() + n - i <= ans) { + return; } - } + if (i >= n) { + ans = max(ans, (int) st.size()); + return; + } + for (int j = i + 1; j <= n; ++j) { + string t = s.substr(i, j - i); + if (!st.contains(t)) { + st.insert(t); + dfs(j); + st.erase(t); + } + } + }; + dfs(0); + return ans; } }; ``` @@ -160,27 +175,57 @@ public: #### Go ```go -func maxUniqueSplit(s string) int { - ans := 1 - vis := map[string]bool{} - - var dfs func(i, t int) - dfs = func(i, t int) { - if i >= len(s) { - ans = max(ans, t) +func maxUniqueSplit(s string) (ans int) { + st := map[string]bool{} + n := len(s) + var dfs func(int) + dfs = func(i int) { + if len(st)+n-i <= ans { + return + } + if i >= n { + ans = max(ans, len(st)) return } - for j := i + 1; j <= len(s); j++ { - x := s[i:j] - if !vis[x] { - vis[x] = true - dfs(j, t+1) - vis[x] = false + for j := i + 1; j <= n; j++ { + if t := s[i:j]; !st[t] { + st[t] = true + dfs(j) + delete(st, t) } } } - dfs(0, 0) - return ans + dfs(0) + return +} +``` + +#### TypeScript + +```ts +function maxUniqueSplit(s: string): number { + const n = s.length; + const st = new Set(); + let ans = 0; + const dfs = (i: number): void => { + if (st.size + n - i <= ans) { + return; + } + if (i >= n) { + ans = Math.max(ans, st.size); + return; + } + for (let j = i + 1; j <= n; ++j) { + const t = s.slice(i, j); + if (!st.has(t)) { + st.add(t); + dfs(j); + st.delete(t); + } + } + }; + dfs(0); + return ans; } ``` diff --git a/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.cpp b/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.cpp index 1b40208ae14a2..2cdebccb97a1d 100644 --- a/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.cpp +++ b/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.cpp @@ -1,27 +1,27 @@ class Solution { public: - unordered_set vis; - string s; - int ans = 1; - int maxUniqueSplit(string s) { - this->s = s; - dfs(0, 0); - return ans; - } - - void dfs(int i, int t) { - if (i >= s.size()) { - ans = max(ans, t); - return; - } - for (int j = i + 1; j <= s.size(); ++j) { - string x = s.substr(i, j - i); - if (!vis.count(x)) { - vis.insert(x); - dfs(j, t + 1); - vis.erase(x); + unordered_set st; + int n = s.size(); + int ans = 0; + auto dfs = [&](this auto&& dfs, int i) -> void { + if (st.size() + n - i <= ans) { + return; + } + if (i >= n) { + ans = max(ans, (int) st.size()); + return; } - } + for (int j = i + 1; j <= n; ++j) { + string t = s.substr(i, j - i); + if (!st.contains(t)) { + st.insert(t); + dfs(j); + st.erase(t); + } + } + }; + dfs(0); + return ans; } -}; \ No newline at end of file +}; diff --git a/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.go b/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.go index 11f73dcfab423..d87805cc1a27a 100644 --- a/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.go +++ b/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.go @@ -1,22 +1,23 @@ -func maxUniqueSplit(s string) int { - ans := 1 - vis := map[string]bool{} - - var dfs func(i, t int) - dfs = func(i, t int) { - if i >= len(s) { - ans = max(ans, t) +func maxUniqueSplit(s string) (ans int) { + st := map[string]bool{} + n := len(s) + var dfs func(int) + dfs = func(i int) { + if len(st)+n-i <= ans { return } - for j := i + 1; j <= len(s); j++ { - x := s[i:j] - if !vis[x] { - vis[x] = true - dfs(j, t+1) - vis[x] = false + if i >= n { + ans = max(ans, len(st)) + return + } + for j := i + 1; j <= n; j++ { + if t := s[i:j]; !st[t] { + st[t] = true + dfs(j) + delete(st, t) } } } - dfs(0, 0) - return ans -} \ No newline at end of file + dfs(0) + return +} diff --git a/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.java b/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.java index 3572d8ddc4ce6..51c18b7e78664 100644 --- a/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.java +++ b/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.java @@ -1,25 +1,28 @@ class Solution { - private Set vis = new HashSet<>(); - private int ans = 1; + private Set st = new HashSet<>(); + private int ans; private String s; public int maxUniqueSplit(String s) { this.s = s; - dfs(0, 0); + dfs(0); return ans; } - private void dfs(int i, int t) { + private void dfs(int i) { + if (st.size() + s.length() - i <= ans) { + return; + } if (i >= s.length()) { - ans = Math.max(ans, t); + ans = Math.max(ans, st.size()); return; } for (int j = i + 1; j <= s.length(); ++j) { - String x = s.substring(i, j); - if (vis.add(x)) { - dfs(j, t + 1); - vis.remove(x); + String t = s.substring(i, j); + if (st.add(t)) { + dfs(j); + st.remove(t); } } } -} \ No newline at end of file +} diff --git a/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.py b/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.py index 7c3c8cec3c495..f2d8e119767be 100644 --- a/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.py +++ b/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.py @@ -1,17 +1,19 @@ class Solution: def maxUniqueSplit(self, s: str) -> int: - def dfs(i, t): + def dfs(i: int): + nonlocal ans + if len(st) + len(s) - i <= ans: + return if i >= len(s): - nonlocal ans - ans = max(ans, t) + ans = max(ans, len(st)) return for j in range(i + 1, len(s) + 1): - if s[i:j] not in vis: - vis.add(s[i:j]) - dfs(j, t + 1) - vis.remove(s[i:j]) + if s[i:j] not in st: + st.add(s[i:j]) + dfs(j) + st.remove(s[i:j]) - vis = set() - ans = 1 - dfs(0, 0) + ans = 0 + st = set() + dfs(0) return ans diff --git a/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.ts b/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.ts new file mode 100644 index 0000000000000..487c09113a5e2 --- /dev/null +++ b/solution/1500-1599/1593.Split a String Into the Max Number of Unique Substrings/Solution.ts @@ -0,0 +1,24 @@ +function maxUniqueSplit(s: string): number { + const n = s.length; + const st = new Set(); + let ans = 0; + const dfs = (i: number): void => { + if (st.size + n - i <= ans) { + return; + } + if (i >= n) { + ans = Math.max(ans, st.size); + return; + } + for (let j = i + 1; j <= n; ++j) { + const t = s.slice(i, j); + if (!st.has(t)) { + st.add(t); + dfs(j); + st.delete(t); + } + } + }; + dfs(0); + return ans; +}