691. 贴纸拼词 : DFS + 记忆化搜索 运用题
题目描述
这是 LeetCode 上的 691. 贴纸拼词 ,难度为 困难。
Tag : 「记忆化搜索」、「DFS」、「状态压缩」、「爆搜」
我们有 nnn 种不同的贴纸。每个贴纸上都有一个小写的英文单词。
您想要拼写出给定的字符串 target
,方法是从收集的贴纸中切割单个字母并重新排列它们。如果你愿意,你可以多次使用每个贴纸,每个贴纸的数量是无限的。
返回你需要拼出 target
的最小贴纸数量。如果任务不可能,则返回 −1-1−1 。
注意:在所有的测试用例中,所有的单词都是从 100010001000 个最常见的美国英语单词中随机选择的,并且 target
被选择为两个随机单词的连接。
示例 1:
输入: stickers = ["with","example","science"], target = "thehat"
输出:3
解释:
我们可以使用 2 个 "with" 贴纸,和 1 个 "example" 贴纸。
把贴纸上的字母剪下来并重新排列后,就可以形成目标 “thehat“ 了。
此外,这是形成目标字符串所需的最小贴纸数量。
示例 2:
输入:stickers = ["notice","possible"], target = "basicbasic"
输出:-1
解释:我们不能通过剪切给定贴纸的字母来形成目标“basicbasic”。
提示:
- n==stickers.lengthn == stickers.lengthn==stickers.length
- 1<=n<=501 <= n <= 501<=n<=50
- 1<=stickers[i].length<=101 <= stickers[i].length <= 101<=stickers[i].length<=10
- 1<=target<=151 <= target <= 151<=target<=15
stickers[i]
和target
由小写英文单词组成
DFS + 记忆化搜索
为了方便,我们记 ss=stickersss = stickersss=stickers,t=targett = targett=target,其中 ttt 的长度为 nnn。
我们使用一个 statestatestate(一个 int
类型变量)来代表当前 ttt 的凑成情况:若 t[i]t[i]t[i] 已被凑成,则在 statestatestate 中低 iii 位为 111,否则为 000。
起始时有 state = 0
,最终若能凑成 ttt,则有 state = (1 << n) - 1
。
由于每个 ss[i]ss[i]ss[i] 可以被使用多次,因此对于一个特定的 statestatestate 而言,其转换为最终的 (1 << n) - 1
的最小步数固定,因此我们可以使用「记忆化搜索」来避免对相同的 statestatestate 进行重复搜索。
而在单步的搜索过程中,我们枚举每个 ss[i]ss[i]ss[i] 来更新 statestatestate,假设使用某个 ss[i]ss[i]ss[i] 得到的新状态为 nstatenstatenstate,则所有的 dfs(nstate) + 1
的最小值即是 f[state]f[state]f[state]。
代码:
class Solution {
int N = 20, M = 1 << 20, INF = 50;
int[] f = new int[M];
String[] ss;
String t;
int dfs(int state) {
int n = t.length();
if (state == ((1 << n) - 1)) return 0;
if (f[state] != -1) return f[state];
int ans = INF;
for (String s : ss) {
int nstate = state;
out:for (char c : s.toCharArray()) {
for (int i = 0; i < n; i++) {
if (t.charAt(i) == c && ((nstate >> i) & 1) == 0) {
nstate |= (1 << i);
continue out;
}
}
}
if (nstate != state) ans = Math.min(ans, dfs(nstate) + 1);
}
return f[state] = ans;
}
public int minStickers(String[] stickers, String target) {
ss = stickers; t = target;
Arrays.fill(f, -1);
int ans = dfs(0);
return ans == INF ? -1 : ans;
}
}
- 时间复杂度:令 nnn 和 mmm 分别代表字符串
t
的长度和数组ss
的长度。共有 2n2^n2n 个状态,单次状态的计算复杂度为 O(∑i=0m−1ss[i].length×n)O(\sum_{i = 0}^{m - 1}ss[i].length \times n)O(∑i=0m−1ss[i].length×n)。整体复杂度为 O(2n×∑i=0m−1ss[i].length×n)O(2^n \times \sum_{i = 0}^{m - 1}ss[i].length \times n)O(2n×∑i=0m−1ss[i].length×n) - 空间复杂度:O(2n)O(2^n)O(2n)
最后
这是我们「刷穿 LeetCode」系列文章的第 No.691
篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour… 。
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
转载自:https://juejin.cn/post/7097431542317711397