diff --git a/solution/0600-0699/0691.Stickers to Spell Word/README.md b/solution/0600-0699/0691.Stickers to Spell Word/README.md index ebf65ba77fbd3..999fb2382e15b 100644 --- a/solution/0600-0699/0691.Stickers to Spell Word/README.md +++ b/solution/0600-0699/0691.Stickers to Spell Word/README.md @@ -69,6 +69,12 @@ tags: ### 方法一:BFS + 状态压缩 +我们注意到,字符串 $\text{target}$ 的长度不超过 $15$,我们可以使用一个长度为 $15$ 的二进制数来表示 $\text{target}$ 的每个字符是否被拼出,如果第 $i$ 位为 $1$,表示 $\text{target}$ 的第 $i$ 个字符已经被拼出,否则表示未被拼出。 + +我们定义一个初始状态 $0$,表示所有字符都未被拼出,然后我们使用广度优先搜索的方法,从初始状态开始,每次搜索时,我们枚举所有的贴纸,对于每一张贴纸,我们尝试拼出 $\text{target}$ 的每一个字符,如果拼出了某个字符,我们就将对应的二进制数的第 $i$ 位设置为 $1$,表示该字符已经被拼出,然后我们继续搜索,直到我们拼出了 $\text{target}$ 的所有字符。 + +时间复杂度 $O(2^n \times m \times (l + n))$,空间复杂度 $O(2^n)$。其中 $n$ 是字符串 $\text{target}$ 的长度,而 $m$ 和 $l$ 分别是贴纸的数量和贴纸的平均长度。 + #### Python3 @@ -76,23 +82,23 @@ tags: ```python class Solution: def minStickers(self, stickers: List[str], target: str) -> int: - q = deque([0]) - ans = 0 n = len(target) + q = deque([0]) vis = [False] * (1 << n) vis[0] = True + ans = 0 while q: for _ in range(len(q)): - state = q.popleft() - if state == (1 << n) - 1: + cur = q.popleft() + if cur == (1 << n) - 1: return ans for s in stickers: - nxt = state cnt = Counter(s) + nxt = cur for i, c in enumerate(target): - if not (nxt & (1 << i)) and cnt[c]: - nxt |= 1 << i + if (cur >> i & 1) == 0 and cnt[c] > 0: cnt[c] -= 1 + nxt |= 1 << i if not vis[nxt]: vis[nxt] = True q.append(nxt) @@ -105,29 +111,28 @@ class Solution: ```java class Solution { public int minStickers(String[] stickers, String target) { + int n = target.length(); Deque q = new ArrayDeque<>(); q.offer(0); - int ans = 0; - int n = target.length(); boolean[] vis = new boolean[1 << n]; vis[0] = true; - while (!q.isEmpty()) { - for (int t = q.size(); t > 0; --t) { - int state = q.poll(); - if (state == (1 << n) - 1) { + for (int ans = 0; !q.isEmpty(); ++ans) { + for (int m = q.size(); m > 0; --m) { + int cur = q.poll(); + if (cur == (1 << n) - 1) { return ans; } for (String s : stickers) { - int nxt = state; int[] cnt = new int[26]; + int nxt = cur; for (char c : s.toCharArray()) { ++cnt[c - 'a']; } for (int i = 0; i < n; ++i) { - int idx = target.charAt(i) - 'a'; - if ((nxt & (1 << i)) == 0 && cnt[idx] > 0) { + int j = target.charAt(i) - 'a'; + if ((cur >> i & 1) == 0 && cnt[j] > 0) { + --cnt[j]; nxt |= 1 << i; - --cnt[idx]; } } if (!vis[nxt]) { @@ -136,7 +141,6 @@ class Solution { } } } - ++ans; } return -1; } @@ -149,25 +153,28 @@ class Solution { class Solution { public: int minStickers(vector& stickers, string target) { - queue q{{0}}; - int ans = 0; int n = target.size(); + queue q{{0}}; vector vis(1 << n); vis[0] = true; - while (!q.empty()) { - for (int t = q.size(); t; --t) { - int state = q.front(); - if (state == (1 << n) - 1) return ans; + for (int ans = 0; q.size(); ++ans) { + for (int m = q.size(); m; --m) { + int cur = q.front(); q.pop(); + if (cur == (1 << n) - 1) { + return ans; + } for (auto& s : stickers) { - int nxt = state; - vector cnt(26); - for (char& c : s) ++cnt[c - 'a']; + int cnt[26]{}; + int nxt = cur; + for (char& c : s) { + ++cnt[c - 'a']; + } for (int i = 0; i < n; ++i) { - int idx = target[i] - 'a'; - if (!(nxt & (1 << i)) && cnt[idx]) { + int j = target[i] - 'a'; + if ((cur >> i & 1) == 0 && cnt[j] > 0) { nxt |= 1 << i; - --cnt[idx]; + --cnt[j]; } } if (!vis[nxt]) { @@ -176,7 +183,6 @@ public: } } } - ++ans; } return -1; } @@ -186,30 +192,28 @@ public: #### Go ```go -func minStickers(stickers []string, target string) int { - q := []int{0} +func minStickers(stickers []string, target string) (ans int) { n := len(target) + q := []int{0} vis := make([]bool, 1< 0 { - for t := len(q); t > 0; t-- { - state := q[0] - if state == (1< 0; ans++ { + for m := len(q); m > 0; m-- { + cur := q[0] q = q[1:] + if cur == 1< 0 { + if cur>>i&1 == 0 && cnt[c-'a'] > 0 { nxt |= 1 << i - cnt[idx]-- + cnt[c-'a']-- } } if !vis[nxt] { @@ -218,12 +222,50 @@ func minStickers(stickers []string, target string) int { } } } - ans++ } return -1 } ``` +#### TypeScript + +```ts +function minStickers(stickers: string[], target: string): number { + const n = target.length; + const q: number[] = [0]; + const vis: boolean[] = Array(1 << n).fill(false); + vis[0] = true; + for (let ans = 0; q.length; ++ans) { + const qq: number[] = []; + for (const cur of q) { + if (cur === (1 << n) - 1) { + return ans; + } + for (const s of stickers) { + const cnt: number[] = Array(26).fill(0); + for (const c of s) { + cnt[c.charCodeAt(0) - 97]++; + } + let nxt = cur; + for (let i = 0; i < n; ++i) { + const j = target.charCodeAt(i) - 97; + if (((cur >> i) & 1) === 0 && cnt[j]) { + nxt |= 1 << i; + cnt[j]--; + } + } + if (!vis[nxt]) { + vis[nxt] = true; + qq.push(nxt); + } + } + } + q.splice(0, q.length, ...qq); + } + return -1; +} +``` + #### Rust ```rust diff --git a/solution/0600-0699/0691.Stickers to Spell Word/README_EN.md b/solution/0600-0699/0691.Stickers to Spell Word/README_EN.md index 989b15e07db15..d63b3179a210b 100644 --- a/solution/0600-0699/0691.Stickers to Spell Word/README_EN.md +++ b/solution/0600-0699/0691.Stickers to Spell Word/README_EN.md @@ -67,7 +67,13 @@ We cannot form the target "basicbasic" from cutting letters from the g -### Solution 1 +### Solution 1: BFS + State Compression + +We notice that the length of the string `target` does not exceed 15. We can use a binary number of length 15 to represent whether each character of `target` has been spelled out. If the $i$th bit is 1, it means that the $i$th character of `target` has been spelled out; otherwise, it has not been spelled out. + +We define an initial state 0, which means that all characters have not been spelled out. Then we use the Breadth-First Search (BFS) method, starting from the initial state. Each time we search, we enumerate all the stickers. For each sticker, we try to spell out each character of `target`. If we spell out a character, we set the $i$th bit of the corresponding binary number to 1, indicating that the character has been spelled out. Then we continue to search until we spell out all the characters of `target`. + +The time complexity is $O(2^n \times m \times (l + n))$, and the space complexity is $O(2^n)$. Where $n$ is the length of the string `target`, and $m$ and $l$ are the number of stickers and the average length of the stickers, respectively. @@ -76,23 +82,23 @@ We cannot form the target "basicbasic" from cutting letters from the g ```python class Solution: def minStickers(self, stickers: List[str], target: str) -> int: - q = deque([0]) - ans = 0 n = len(target) + q = deque([0]) vis = [False] * (1 << n) vis[0] = True + ans = 0 while q: for _ in range(len(q)): - state = q.popleft() - if state == (1 << n) - 1: + cur = q.popleft() + if cur == (1 << n) - 1: return ans for s in stickers: - nxt = state cnt = Counter(s) + nxt = cur for i, c in enumerate(target): - if not (nxt & (1 << i)) and cnt[c]: - nxt |= 1 << i + if (cur >> i & 1) == 0 and cnt[c] > 0: cnt[c] -= 1 + nxt |= 1 << i if not vis[nxt]: vis[nxt] = True q.append(nxt) @@ -105,29 +111,28 @@ class Solution: ```java class Solution { public int minStickers(String[] stickers, String target) { + int n = target.length(); Deque q = new ArrayDeque<>(); q.offer(0); - int ans = 0; - int n = target.length(); boolean[] vis = new boolean[1 << n]; vis[0] = true; - while (!q.isEmpty()) { - for (int t = q.size(); t > 0; --t) { - int state = q.poll(); - if (state == (1 << n) - 1) { + for (int ans = 0; !q.isEmpty(); ++ans) { + for (int m = q.size(); m > 0; --m) { + int cur = q.poll(); + if (cur == (1 << n) - 1) { return ans; } for (String s : stickers) { - int nxt = state; int[] cnt = new int[26]; + int nxt = cur; for (char c : s.toCharArray()) { ++cnt[c - 'a']; } for (int i = 0; i < n; ++i) { - int idx = target.charAt(i) - 'a'; - if ((nxt & (1 << i)) == 0 && cnt[idx] > 0) { + int j = target.charAt(i) - 'a'; + if ((cur >> i & 1) == 0 && cnt[j] > 0) { + --cnt[j]; nxt |= 1 << i; - --cnt[idx]; } } if (!vis[nxt]) { @@ -136,7 +141,6 @@ class Solution { } } } - ++ans; } return -1; } @@ -149,25 +153,28 @@ class Solution { class Solution { public: int minStickers(vector& stickers, string target) { - queue q{{0}}; - int ans = 0; int n = target.size(); + queue q{{0}}; vector vis(1 << n); vis[0] = true; - while (!q.empty()) { - for (int t = q.size(); t; --t) { - int state = q.front(); - if (state == (1 << n) - 1) return ans; + for (int ans = 0; q.size(); ++ans) { + for (int m = q.size(); m; --m) { + int cur = q.front(); q.pop(); + if (cur == (1 << n) - 1) { + return ans; + } for (auto& s : stickers) { - int nxt = state; - vector cnt(26); - for (char& c : s) ++cnt[c - 'a']; + int cnt[26]{}; + int nxt = cur; + for (char& c : s) { + ++cnt[c - 'a']; + } for (int i = 0; i < n; ++i) { - int idx = target[i] - 'a'; - if (!(nxt & (1 << i)) && cnt[idx]) { + int j = target[i] - 'a'; + if ((cur >> i & 1) == 0 && cnt[j] > 0) { nxt |= 1 << i; - --cnt[idx]; + --cnt[j]; } } if (!vis[nxt]) { @@ -176,7 +183,6 @@ public: } } } - ++ans; } return -1; } @@ -186,30 +192,28 @@ public: #### Go ```go -func minStickers(stickers []string, target string) int { - q := []int{0} +func minStickers(stickers []string, target string) (ans int) { n := len(target) + q := []int{0} vis := make([]bool, 1< 0 { - for t := len(q); t > 0; t-- { - state := q[0] - if state == (1< 0; ans++ { + for m := len(q); m > 0; m-- { + cur := q[0] q = q[1:] + if cur == 1< 0 { + if cur>>i&1 == 0 && cnt[c-'a'] > 0 { nxt |= 1 << i - cnt[idx]-- + cnt[c-'a']-- } } if !vis[nxt] { @@ -218,12 +222,50 @@ func minStickers(stickers []string, target string) int { } } } - ans++ } return -1 } ``` +#### TypeScript + +```ts +function minStickers(stickers: string[], target: string): number { + const n = target.length; + const q: number[] = [0]; + const vis: boolean[] = Array(1 << n).fill(false); + vis[0] = true; + for (let ans = 0; q.length; ++ans) { + const qq: number[] = []; + for (const cur of q) { + if (cur === (1 << n) - 1) { + return ans; + } + for (const s of stickers) { + const cnt: number[] = Array(26).fill(0); + for (const c of s) { + cnt[c.charCodeAt(0) - 97]++; + } + let nxt = cur; + for (let i = 0; i < n; ++i) { + const j = target.charCodeAt(i) - 97; + if (((cur >> i) & 1) === 0 && cnt[j]) { + nxt |= 1 << i; + cnt[j]--; + } + } + if (!vis[nxt]) { + vis[nxt] = true; + qq.push(nxt); + } + } + } + q.splice(0, q.length, ...qq); + } + return -1; +} +``` + #### Rust ```rust diff --git a/solution/0600-0699/0691.Stickers to Spell Word/Solution.cpp b/solution/0600-0699/0691.Stickers to Spell Word/Solution.cpp index be673207a008e..8226fbcc22195 100644 --- a/solution/0600-0699/0691.Stickers to Spell Word/Solution.cpp +++ b/solution/0600-0699/0691.Stickers to Spell Word/Solution.cpp @@ -1,25 +1,28 @@ class Solution { public: int minStickers(vector& stickers, string target) { - queue q{{0}}; - int ans = 0; int n = target.size(); + queue q{{0}}; vector vis(1 << n); vis[0] = true; - while (!q.empty()) { - for (int t = q.size(); t; --t) { - int state = q.front(); - if (state == (1 << n) - 1) return ans; + for (int ans = 0; q.size(); ++ans) { + for (int m = q.size(); m; --m) { + int cur = q.front(); q.pop(); + if (cur == (1 << n) - 1) { + return ans; + } for (auto& s : stickers) { - int nxt = state; - vector cnt(26); - for (char& c : s) ++cnt[c - 'a']; + int cnt[26]{}; + int nxt = cur; + for (char& c : s) { + ++cnt[c - 'a']; + } for (int i = 0; i < n; ++i) { - int idx = target[i] - 'a'; - if (!(nxt & (1 << i)) && cnt[idx]) { + int j = target[i] - 'a'; + if ((cur >> i & 1) == 0 && cnt[j] > 0) { nxt |= 1 << i; - --cnt[idx]; + --cnt[j]; } } if (!vis[nxt]) { @@ -28,7 +31,6 @@ class Solution { } } } - ++ans; } return -1; } diff --git a/solution/0600-0699/0691.Stickers to Spell Word/Solution.go b/solution/0600-0699/0691.Stickers to Spell Word/Solution.go index 4ef6698542563..353e13506fb8a 100644 --- a/solution/0600-0699/0691.Stickers to Spell Word/Solution.go +++ b/solution/0600-0699/0691.Stickers to Spell Word/Solution.go @@ -1,27 +1,25 @@ -func minStickers(stickers []string, target string) int { - q := []int{0} +func minStickers(stickers []string, target string) (ans int) { n := len(target) + q := []int{0} vis := make([]bool, 1< 0 { - for t := len(q); t > 0; t-- { - state := q[0] - if state == (1< 0; ans++ { + for m := len(q); m > 0; m-- { + cur := q[0] q = q[1:] + if cur == 1< 0 { + if cur>>i&1 == 0 && cnt[c-'a'] > 0 { nxt |= 1 << i - cnt[idx]-- + cnt[c-'a']-- } } if !vis[nxt] { @@ -30,7 +28,6 @@ func minStickers(stickers []string, target string) int { } } } - ans++ } return -1 } \ No newline at end of file diff --git a/solution/0600-0699/0691.Stickers to Spell Word/Solution.java b/solution/0600-0699/0691.Stickers to Spell Word/Solution.java index b988e0ed80231..2af080a564e9d 100644 --- a/solution/0600-0699/0691.Stickers to Spell Word/Solution.java +++ b/solution/0600-0699/0691.Stickers to Spell Word/Solution.java @@ -1,28 +1,27 @@ class Solution { public int minStickers(String[] stickers, String target) { + int n = target.length(); Deque q = new ArrayDeque<>(); q.offer(0); - int ans = 0; - int n = target.length(); boolean[] vis = new boolean[1 << n]; vis[0] = true; - while (!q.isEmpty()) { - for (int t = q.size(); t > 0; --t) { - int state = q.poll(); - if (state == (1 << n) - 1) { + for (int ans = 0; !q.isEmpty(); ++ans) { + for (int m = q.size(); m > 0; --m) { + int cur = q.poll(); + if (cur == (1 << n) - 1) { return ans; } for (String s : stickers) { - int nxt = state; int[] cnt = new int[26]; + int nxt = cur; for (char c : s.toCharArray()) { ++cnt[c - 'a']; } for (int i = 0; i < n; ++i) { - int idx = target.charAt(i) - 'a'; - if ((nxt & (1 << i)) == 0 && cnt[idx] > 0) { + int j = target.charAt(i) - 'a'; + if ((cur >> i & 1) == 0 && cnt[j] > 0) { + --cnt[j]; nxt |= 1 << i; - --cnt[idx]; } } if (!vis[nxt]) { @@ -31,7 +30,6 @@ public int minStickers(String[] stickers, String target) { } } } - ++ans; } return -1; } diff --git a/solution/0600-0699/0691.Stickers to Spell Word/Solution.py b/solution/0600-0699/0691.Stickers to Spell Word/Solution.py index 83f0b78bc41c2..72b1eb959383d 100644 --- a/solution/0600-0699/0691.Stickers to Spell Word/Solution.py +++ b/solution/0600-0699/0691.Stickers to Spell Word/Solution.py @@ -1,22 +1,22 @@ class Solution: def minStickers(self, stickers: List[str], target: str) -> int: - q = deque([0]) - ans = 0 n = len(target) + q = deque([0]) vis = [False] * (1 << n) vis[0] = True + ans = 0 while q: for _ in range(len(q)): - state = q.popleft() - if state == (1 << n) - 1: + cur = q.popleft() + if cur == (1 << n) - 1: return ans for s in stickers: - nxt = state cnt = Counter(s) + nxt = cur for i, c in enumerate(target): - if not (nxt & (1 << i)) and cnt[c]: - nxt |= 1 << i + if (cur >> i & 1) == 0 and cnt[c] > 0: cnt[c] -= 1 + nxt |= 1 << i if not vis[nxt]: vis[nxt] = True q.append(nxt) diff --git a/solution/0600-0699/0691.Stickers to Spell Word/Solution.ts b/solution/0600-0699/0691.Stickers to Spell Word/Solution.ts new file mode 100644 index 0000000000000..a4ac4567f28db --- /dev/null +++ b/solution/0600-0699/0691.Stickers to Spell Word/Solution.ts @@ -0,0 +1,34 @@ +function minStickers(stickers: string[], target: string): number { + const n = target.length; + const q: number[] = [0]; + const vis: boolean[] = Array(1 << n).fill(false); + vis[0] = true; + for (let ans = 0; q.length; ++ans) { + const qq: number[] = []; + for (const cur of q) { + if (cur === (1 << n) - 1) { + return ans; + } + for (const s of stickers) { + const cnt: number[] = Array(26).fill(0); + for (const c of s) { + cnt[c.charCodeAt(0) - 97]++; + } + let nxt = cur; + for (let i = 0; i < n; ++i) { + const j = target.charCodeAt(i) - 97; + if (((cur >> i) & 1) === 0 && cnt[j]) { + nxt |= 1 << i; + cnt[j]--; + } + } + if (!vis[nxt]) { + vis[nxt] = true; + qq.push(nxt); + } + } + } + q.splice(0, q.length, ...qq); + } + return -1; +}