From 7851eb66c58b1da208a0f3f19cdb48b41236b7ea Mon Sep 17 00:00:00 2001 From: yanglbme Date: Mon, 16 Sep 2024 11:26:40 +0800 Subject: [PATCH] feat: add solutions to lc problem: No.3291 No.3291.Minimum Number of Valid Strings to Form Target I --- .../README.md | 257 +++++++++++++++++- .../README_EN.md | 257 +++++++++++++++++- .../Solution.cpp | 50 ++++ .../Solution.go | 48 ++++ .../Solution.java | 52 ++++ .../Solution.py | 38 +++ .../Solution.ts | 47 ++++ 7 files changed, 741 insertions(+), 8 deletions(-) create mode 100644 solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.cpp create mode 100644 solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.go create mode 100644 solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.java create mode 100644 solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.py create mode 100644 solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.ts diff --git a/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/README.md b/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/README.md index 678d916c9ba94..70ad2b64967e2 100644 --- a/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/README.md +++ b/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/README.md @@ -84,32 +84,281 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3200-3299/3291.Mi -### 方法一 +### 方法一:字典树 + 记忆化搜索 + +我们可以使用字典树存储所有有效字符串,然后使用记忆化搜索计算答案。 + +我们设计一个函数 $\textit{dfs}(i)$,表示从字符串 $\textit{target}$ 的第 $i$ 个字符开始,需要连接的最少字符串数量。那么答案就是 $\textit{dfs}(0)$。 + +函数 $\textit{dfs}(i)$ 的计算方式如下: + +- 如果 $i \geq n$,表示字符串 $\textit{target}$ 已经遍历完了,返回 $0$; +- 否则,我们可以从字典树中找到以 $\textit{target}[i]$ 开头的有效字符串,然后递归计算 $\textit{dfs}(i + \text{len}(w))$,其中 $w$ 是找到的有效字符串。我们取这些值中的最小值加 $1$ 作为 $\textit{dfs}(i)$ 的返回值。 + +为了避免重复计算,我们使用记忆化搜索。 + +时间复杂度 $O(n^2 + L)$,空间复杂度 $O(n + L)$。其中 $n$ 是字符串 $\textit{target}$ 的长度,而 $L$ 是所有有效字符串的总长度。 #### Python3 ```python - +def min(a: int, b: int) -> int: + return a if a < b else b + + +class Trie: + def __init__(self): + self.children: List[Optional[Trie]] = [None] * 26 + + def insert(self, w: str): + node = self + for i in map(lambda c: ord(c) - 97, w): + if node.children[i] is None: + node.children[i] = Trie() + node = node.children[i] + + +class Solution: + def minValidStrings(self, words: List[str], target: str) -> int: + @cache + def dfs(i: int) -> int: + if i >= n: + return 0 + node = trie + ans = inf + for j in range(i, n): + k = ord(target[j]) - 97 + if node.children[k] is None: + break + node = node.children[k] + ans = min(ans, 1 + dfs(j + 1)) + return ans + + trie = Trie() + for w in words: + trie.insert(w) + n = len(target) + ans = dfs(0) + return ans if ans < inf else -1 ``` #### Java ```java - +class Trie { + Trie[] children = new Trie[26]; + + void insert(String w) { + Trie node = this; + for (int i = 0; i < w.length(); ++i) { + int j = w.charAt(i) - 'a'; + if (node.children[j] == null) { + node.children[j] = new Trie(); + } + node = node.children[j]; + } + } +} + +class Solution { + private Integer[] f; + private char[] s; + private Trie trie; + private final int inf = 1 << 30; + + public int minValidStrings(String[] words, String target) { + trie = new Trie(); + for (String w : words) { + trie.insert(w); + } + s = target.toCharArray(); + f = new Integer[s.length]; + int ans = dfs(0); + return ans < inf ? ans : -1; + } + + private int dfs(int i) { + if (i >= s.length) { + return 0; + } + if (f[i] != null) { + return f[i]; + } + Trie node = trie; + f[i] = inf; + for (int j = i; j < s.length; ++j) { + int k = s[j] - 'a'; + if (node.children[k] == null) { + break; + } + f[i] = Math.min(f[i], 1 + dfs(j + 1)); + node = node.children[k]; + } + return f[i]; + } +} ``` #### C++ ```cpp - +class Trie { +public: + Trie* children[26]{}; + + void insert(string& word) { + Trie* node = this; + for (char& c : word) { + int i = c - 'a'; + if (!node->children[i]) { + node->children[i] = new Trie(); + } + node = node->children[i]; + } + } +}; + +class Solution { +public: + int minValidStrings(vector& words, string target) { + int n = target.size(); + Trie* trie = new Trie(); + for (auto& w : words) { + trie->insert(w); + } + const int inf = 1 << 30; + int f[n]; + memset(f, -1, sizeof(f)); + auto dfs = [&](auto&& dfs, int i) -> int { + if (i >= n) { + return 0; + } + if (f[i] != -1) { + return f[i]; + } + f[i] = inf; + Trie* node = trie; + for (int j = i; j < n; ++j) { + int k = target[j] - 'a'; + if (!node->children[k]) { + break; + } + node = node->children[k]; + f[i] = min(f[i], 1 + dfs(dfs, j + 1)); + } + return f[i]; + }; + int ans = dfs(dfs, 0); + return ans < inf ? ans : -1; + } +}; ``` #### Go ```go +type Trie struct { + children [26]*Trie +} + +func (t *Trie) insert(word string) { + node := t + for _, c := range word { + idx := c - 'a' + if node.children[idx] == nil { + node.children[idx] = &Trie{} + } + node = node.children[idx] + } +} + +func minValidStrings(words []string, target string) int { + n := len(target) + trie := &Trie{} + for _, w := range words { + trie.insert(w) + } + const inf int = 1 << 30 + f := make([]int, n) + var dfs func(int) int + dfs = func(i int) int { + if i >= n { + return 0 + } + if f[i] != 0 { + return f[i] + } + node := trie + f[i] = inf + for j := i; j < n; j++ { + k := int(target[j] - 'a') + if node.children[k] == nil { + break + } + f[i] = min(f[i], 1+dfs(j+1)) + node = node.children[k] + } + return f[i] + } + if ans := dfs(0); ans < inf { + return ans + } + return -1 +} +``` +#### TypeScript + +```ts +class Trie { + children: (Trie | null)[] = Array(26).fill(null); + + insert(word: string): void { + let node: Trie = this; + for (const c of word) { + const i = c.charCodeAt(0) - 'a'.charCodeAt(0); + if (!node.children[i]) { + node.children[i] = new Trie(); + } + node = node.children[i]; + } + } +} + +function minValidStrings(words: string[], target: string): number { + const n = target.length; + const trie = new Trie(); + for (const w of words) { + trie.insert(w); + } + const inf = 1 << 30; + const f = Array(n).fill(0); + + const dfs = (i: number): number => { + if (i >= n) { + return 0; + } + if (f[i]) { + return f[i]; + } + f[i] = inf; + let node: Trie | null = trie; + for (let j = i; j < n; ++j) { + const k = target[j].charCodeAt(0) - 'a'.charCodeAt(0); + if (!node?.children[k]) { + break; + } + node = node.children[k]; + f[i] = Math.min(f[i], 1 + dfs(j + 1)); + } + return f[i]; + }; + + const ans = dfs(0); + return ans < inf ? ans : -1; +} ``` diff --git a/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/README_EN.md b/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/README_EN.md index 5e305b4e1414d..8299b3cb71fad 100644 --- a/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/README_EN.md +++ b/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/README_EN.md @@ -84,32 +84,281 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3200-3299/3291.Mi -### Solution 1 +### Solution 1: Trie + Memoization + +We can use a trie to store all valid strings and then use memoization to calculate the answer. + +We design a function $\textit{dfs}(i)$, which represents the minimum number of strings needed to concatenate starting from the $i$-th character of the string $\textit{target}$. The answer is $\textit{dfs}(0)$. + +The function $\textit{dfs}(i)$ is calculated as follows: + +- If $i \geq n$, it means the string $\textit{target}$ has been completely traversed, so we return $0$; +- Otherwise, we can find valid strings in the trie that start with $\textit{target}[i]$, and then recursively calculate $\textit{dfs}(i + \text{len}(w))$, where $w$ is the valid string found. We take the minimum of these values and add $1$ as the return value of $\textit{dfs}(i)$. + +To avoid redundant calculations, we use memoization. + +The time complexity is $O(n^2 + L)$, and the space complexity is $O(n + L)$. Here, $n$ is the length of the string $\textit{target}$, and $L$ is the total length of all valid strings. #### Python3 ```python - +def min(a: int, b: int) -> int: + return a if a < b else b + + +class Trie: + def __init__(self): + self.children: List[Optional[Trie]] = [None] * 26 + + def insert(self, w: str): + node = self + for i in map(lambda c: ord(c) - 97, w): + if node.children[i] is None: + node.children[i] = Trie() + node = node.children[i] + + +class Solution: + def minValidStrings(self, words: List[str], target: str) -> int: + @cache + def dfs(i: int) -> int: + if i >= n: + return 0 + node = trie + ans = inf + for j in range(i, n): + k = ord(target[j]) - 97 + if node.children[k] is None: + break + node = node.children[k] + ans = min(ans, 1 + dfs(j + 1)) + return ans + + trie = Trie() + for w in words: + trie.insert(w) + n = len(target) + ans = dfs(0) + return ans if ans < inf else -1 ``` #### Java ```java - +class Trie { + Trie[] children = new Trie[26]; + + void insert(String w) { + Trie node = this; + for (int i = 0; i < w.length(); ++i) { + int j = w.charAt(i) - 'a'; + if (node.children[j] == null) { + node.children[j] = new Trie(); + } + node = node.children[j]; + } + } +} + +class Solution { + private Integer[] f; + private char[] s; + private Trie trie; + private final int inf = 1 << 30; + + public int minValidStrings(String[] words, String target) { + trie = new Trie(); + for (String w : words) { + trie.insert(w); + } + s = target.toCharArray(); + f = new Integer[s.length]; + int ans = dfs(0); + return ans < inf ? ans : -1; + } + + private int dfs(int i) { + if (i >= s.length) { + return 0; + } + if (f[i] != null) { + return f[i]; + } + Trie node = trie; + f[i] = inf; + for (int j = i; j < s.length; ++j) { + int k = s[j] - 'a'; + if (node.children[k] == null) { + break; + } + f[i] = Math.min(f[i], 1 + dfs(j + 1)); + node = node.children[k]; + } + return f[i]; + } +} ``` #### C++ ```cpp - +class Trie { +public: + Trie* children[26]{}; + + void insert(string& word) { + Trie* node = this; + for (char& c : word) { + int i = c - 'a'; + if (!node->children[i]) { + node->children[i] = new Trie(); + } + node = node->children[i]; + } + } +}; + +class Solution { +public: + int minValidStrings(vector& words, string target) { + int n = target.size(); + Trie* trie = new Trie(); + for (auto& w : words) { + trie->insert(w); + } + const int inf = 1 << 30; + int f[n]; + memset(f, -1, sizeof(f)); + auto dfs = [&](auto&& dfs, int i) -> int { + if (i >= n) { + return 0; + } + if (f[i] != -1) { + return f[i]; + } + f[i] = inf; + Trie* node = trie; + for (int j = i; j < n; ++j) { + int k = target[j] - 'a'; + if (!node->children[k]) { + break; + } + node = node->children[k]; + f[i] = min(f[i], 1 + dfs(dfs, j + 1)); + } + return f[i]; + }; + int ans = dfs(dfs, 0); + return ans < inf ? ans : -1; + } +}; ``` #### Go ```go +type Trie struct { + children [26]*Trie +} + +func (t *Trie) insert(word string) { + node := t + for _, c := range word { + idx := c - 'a' + if node.children[idx] == nil { + node.children[idx] = &Trie{} + } + node = node.children[idx] + } +} + +func minValidStrings(words []string, target string) int { + n := len(target) + trie := &Trie{} + for _, w := range words { + trie.insert(w) + } + const inf int = 1 << 30 + f := make([]int, n) + var dfs func(int) int + dfs = func(i int) int { + if i >= n { + return 0 + } + if f[i] != 0 { + return f[i] + } + node := trie + f[i] = inf + for j := i; j < n; j++ { + k := int(target[j] - 'a') + if node.children[k] == nil { + break + } + f[i] = min(f[i], 1+dfs(j+1)) + node = node.children[k] + } + return f[i] + } + if ans := dfs(0); ans < inf { + return ans + } + return -1 +} +``` +#### TypeScript + +```ts +class Trie { + children: (Trie | null)[] = Array(26).fill(null); + + insert(word: string): void { + let node: Trie = this; + for (const c of word) { + const i = c.charCodeAt(0) - 'a'.charCodeAt(0); + if (!node.children[i]) { + node.children[i] = new Trie(); + } + node = node.children[i]; + } + } +} + +function minValidStrings(words: string[], target: string): number { + const n = target.length; + const trie = new Trie(); + for (const w of words) { + trie.insert(w); + } + const inf = 1 << 30; + const f = Array(n).fill(0); + + const dfs = (i: number): number => { + if (i >= n) { + return 0; + } + if (f[i]) { + return f[i]; + } + f[i] = inf; + let node: Trie | null = trie; + for (let j = i; j < n; ++j) { + const k = target[j].charCodeAt(0) - 'a'.charCodeAt(0); + if (!node?.children[k]) { + break; + } + node = node.children[k]; + f[i] = Math.min(f[i], 1 + dfs(j + 1)); + } + return f[i]; + }; + + const ans = dfs(0); + return ans < inf ? ans : -1; +} ``` diff --git a/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.cpp b/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.cpp new file mode 100644 index 0000000000000..5bee2eb40f24c --- /dev/null +++ b/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.cpp @@ -0,0 +1,50 @@ +class Trie { +public: + Trie* children[26]{}; + + void insert(string& word) { + Trie* node = this; + for (char& c : word) { + int i = c - 'a'; + if (!node->children[i]) { + node->children[i] = new Trie(); + } + node = node->children[i]; + } + } +}; + +class Solution { +public: + int minValidStrings(vector& words, string target) { + int n = target.size(); + Trie* trie = new Trie(); + for (auto& w : words) { + trie->insert(w); + } + const int inf = 1 << 30; + int f[n]; + memset(f, -1, sizeof(f)); + auto dfs = [&](auto&& dfs, int i) -> int { + if (i >= n) { + return 0; + } + if (f[i] != -1) { + return f[i]; + } + f[i] = inf; + Trie* node = trie; + for (int j = i; j < n; ++j) { + int k = target[j] - 'a'; + if (!node->children[k]) { + break; + } + node = node->children[k]; + f[i] = min(f[i], 1 + dfs(dfs, j + 1)); + } + return f[i]; + }; + int ans = dfs(dfs, 0); + return ans < inf ? ans : -1; + } +}; diff --git a/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.go b/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.go new file mode 100644 index 0000000000000..3be18fbd8a908 --- /dev/null +++ b/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.go @@ -0,0 +1,48 @@ +type Trie struct { + children [26]*Trie +} + +func (t *Trie) insert(word string) { + node := t + for _, c := range word { + idx := c - 'a' + if node.children[idx] == nil { + node.children[idx] = &Trie{} + } + node = node.children[idx] + } +} + +func minValidStrings(words []string, target string) int { + n := len(target) + trie := &Trie{} + for _, w := range words { + trie.insert(w) + } + const inf int = 1 << 30 + f := make([]int, n) + var dfs func(int) int + dfs = func(i int) int { + if i >= n { + return 0 + } + if f[i] != 0 { + return f[i] + } + node := trie + f[i] = inf + for j := i; j < n; j++ { + k := int(target[j] - 'a') + if node.children[k] == nil { + break + } + f[i] = min(f[i], 1+dfs(j+1)) + node = node.children[k] + } + return f[i] + } + if ans := dfs(0); ans < inf { + return ans + } + return -1 +} diff --git a/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.java b/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.java new file mode 100644 index 0000000000000..40bf212fcafad --- /dev/null +++ b/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.java @@ -0,0 +1,52 @@ +class Trie { + Trie[] children = new Trie[26]; + + void insert(String w) { + Trie node = this; + for (int i = 0; i < w.length(); ++i) { + int j = w.charAt(i) - 'a'; + if (node.children[j] == null) { + node.children[j] = new Trie(); + } + node = node.children[j]; + } + } +} + +class Solution { + private Integer[] f; + private char[] s; + private Trie trie; + private final int inf = 1 << 30; + + public int minValidStrings(String[] words, String target) { + trie = new Trie(); + for (String w : words) { + trie.insert(w); + } + s = target.toCharArray(); + f = new Integer[s.length]; + int ans = dfs(0); + return ans < inf ? ans : -1; + } + + private int dfs(int i) { + if (i >= s.length) { + return 0; + } + if (f[i] != null) { + return f[i]; + } + Trie node = trie; + f[i] = inf; + for (int j = i; j < s.length; ++j) { + int k = s[j] - 'a'; + if (node.children[k] == null) { + break; + } + f[i] = Math.min(f[i], 1 + dfs(j + 1)); + node = node.children[k]; + } + return f[i]; + } +} diff --git a/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.py b/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.py new file mode 100644 index 0000000000000..a558adf49e8ad --- /dev/null +++ b/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.py @@ -0,0 +1,38 @@ +def min(a: int, b: int) -> int: + return a if a < b else b + + +class Trie: + def __init__(self): + self.children: List[Optional[Trie]] = [None] * 26 + + def insert(self, w: str): + node = self + for i in map(lambda c: ord(c) - 97, w): + if node.children[i] is None: + node.children[i] = Trie() + node = node.children[i] + + +class Solution: + def minValidStrings(self, words: List[str], target: str) -> int: + @cache + def dfs(i: int) -> int: + if i >= n: + return 0 + node = trie + ans = inf + for j in range(i, n): + k = ord(target[j]) - 97 + if node.children[k] is None: + break + node = node.children[k] + ans = min(ans, 1 + dfs(j + 1)) + return ans + + trie = Trie() + for w in words: + trie.insert(w) + n = len(target) + ans = dfs(0) + return ans if ans < inf else -1 diff --git a/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.ts b/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.ts new file mode 100644 index 0000000000000..6086db275af22 --- /dev/null +++ b/solution/3200-3299/3291.Minimum Number of Valid Strings to Form Target I/Solution.ts @@ -0,0 +1,47 @@ +class Trie { + children: (Trie | null)[] = Array(26).fill(null); + + insert(word: string): void { + let node: Trie = this; + for (const c of word) { + const i = c.charCodeAt(0) - 'a'.charCodeAt(0); + if (!node.children[i]) { + node.children[i] = new Trie(); + } + node = node.children[i]; + } + } +} + +function minValidStrings(words: string[], target: string): number { + const n = target.length; + const trie = new Trie(); + for (const w of words) { + trie.insert(w); + } + const inf = 1 << 30; + const f = Array(n).fill(0); + + const dfs = (i: number): number => { + if (i >= n) { + return 0; + } + if (f[i]) { + return f[i]; + } + f[i] = inf; + let node: Trie | null = trie; + for (let j = i; j < n; ++j) { + const k = target[j].charCodeAt(0) - 'a'.charCodeAt(0); + if (!node?.children[k]) { + break; + } + node = node.children[k]; + f[i] = Math.min(f[i], 1 + dfs(j + 1)); + } + return f[i]; + }; + + const ans = dfs(0); + return ans < inf ? ans : -1; +}