题目地址:
https://leetcode.com/problems/stickers-to-spell-word/
给定 n n n个不同的单词,以字符串数组 A A A给出,再给定一个目标单词 s s s,问至少要从 A A A中挑出多少个单词,可以使得这些单词的总共的字母构成完全覆盖 s s s的字母构成。可以重复挑选。如果无解则返回 − 1 -1 −1。
思路是状态压缩 + 记忆化搜索。用二进制位表示 s s s的右起第 k k k位是否覆盖,那么每个二进制数就能表示 s s s中已经被覆盖了哪些字母。那么状态 11...1 11...1 11...1一共 l s l_s ls个 1 1 1就表示整个 s s s都被覆盖了这种状态,即我们需要达到的状态。设 f [ x ] f[x] f[x]是从 x x x这个状态到达终点状态还需要多少个单词,那么可以枚举每个单词,如果该单词不能使得 x x x这个状态发生改变,则略过(比如这个单词不能覆盖还未覆盖的字母,那这个单词是没用的);否则加入该单词,然后继续DFS进入下一层,继续枚举加入每个单词,直到到达终止状态为止。如果到不了则说明无解,返回 − 1 -1 −1。这里有可能某个状态被重复搜索多次,可以使用记忆化,每次在 f f f里查询记忆,算出答案之前要保存记忆。此外,某个状态加入某个字母会到达哪个状态,这个信息也经常要被重复调用,也可以做记忆化存储。代码如下:
import java.util.Arrays;
public class Solution {
public int minStickers(String[] stickers, String target) {
int n = target.length();
// f[x]表示从x这个状态走到全是1的状态要多少步,即需要多少个单词
int[] f = new int[1 << n];
// g[x][ch]表示将ch加入x这个状态能得到的新的状态是什么
int[][] g = new int[1 << n][26];
Arrays.fill(f, -1);
for (int[] row : g) {
Arrays.fill(row, -1);
}
// 初始状态是0
int res = dfs(0, f, g, stickers, target);
return res == 0x3f3f3f3f ? -1 : res;
}
private int dfs(int state, int[] f, int[][] g, String[] strs, String target) {
// 有记忆则调取记忆
if (f[state] != -1) {
return f[state];
}
// 走到终点了,则不需要新的单词了,做记忆并返回
if (state == (1 << target.length()) - 1) {
return f[state] = 0;
}
int res = 0x3f3f3f3f;
// 开始枚举本层DFS添加的单词
for (String s : strs) {
int cur = state;
// 将该单词加入state得到新的状态cur
for (int i = 0; i < s.length(); i++) {
cur = fill(cur, s.charAt(i), g, target);
}
// 如果cur和原来的state相等,则说明添加当前单词没有效果,略过该单词
if (cur == state) {
continue;
}
// 否则枚举添加该单词的情形,继续DFS,并更新答案
res = Math.min(res, dfs(cur, f, g, strs, target) + 1);
}
// 返回前做记忆
return f[state] = res;
}
// 返回state添加字母ch后到达的状态
private int fill(int state, char ch, int[][] g, String target) {
if (g[state][ch - 'a'] != -1) {
return g[state][ch - 'a'];
}
int cur = state;
for (int i = 0; i < target.length(); i++) {
// 这里只需要找到第一个可以填充的位置就可以了,
// 因为本质上同一个字母填充的顺序是不影响答案的
if ((state >> i & 1) == 0 && ch == target.charAt(i)) {
cur += 1 << i;
break;
}
}
// 做记忆化存储并返回
return g[state][ch - 'a'] = cur;
}
}
时间复杂度 O ( n 2 l s ) O(n2^{l_s}) O(n2ls),空间 O ( 2 l s ) O(2^{l_s}) O(2ls)。