题目描述
你想要用小写字母组成一个目标字符串 target
。
开始的时候,序列由 target.length
个 '?'
记号组成。而你有一个小写字母印章 stamp
。
在每个回合,你可以将印章放在序列上,并将序列中的每个字母替换为印章上的相应字母。你最多可以进行 10 * target.length
个回合。
举个例子,如果初始序列为 "?????",而你的印章 stamp
是 "abc"
,那么在第一回合,你可以得到 "abc??"、"?abc?"、"??abc"。(请注意,印章必须完全包含在序列的边界内才能盖下去。)
如果可以印出序列,那么返回一个数组,该数组由每个回合中被印下的最左边字母的索引组成。如果不能印出序列,就返回一个空数组。
例如,如果序列是 "ababc",印章是 "abc"
,那么我们就可以返回与操作 "?????" -> "abc??" -> "ababc" 相对应的答案 [0, 2]
;
另外,如果可以印出序列,那么需要保证可以在 10 * target.length
个回合内完成。任何超过此数字的答案将不被接受。
示例 1:
输入:stamp = "abc", target = "ababc" 输出:[0,2] ([1,0,2] 以及其他一些可能的结果也将作为答案被接受)
示例 2:
输入:stamp = "abca", target = "aabcaca" 输出:[3,0,1]
提示:
1 <= stamp.length <= target.length <= 1000
stamp
和target
只包含小写字母。
解题思路
-
初始化变量:
m
和n
分别代表stamp
和target
的长度。a
存储每个可能覆盖起始位置的匹配集(matched
)和不匹配集(unmatched
)。adone
是一个布尔数组,标记每个起始位置是否已经处理完毕。q
是一个队列,用于存放已完全匹配的起始位置,以进行广度优先搜索(BFS)。
-
遍历
target
构建匹配集合:- 对
target
进行遍历,对每个可能的起始位置检查stamp
是否匹配。 - 对于每个位置,遍历
stamp
的每个字符,构建匹配集和不匹配集。 - 如果某个起始位置完全匹配(不匹配集为空),则加入队列并记录在
ans
中。
- 对
-
广度优先搜索处理完全匹配:
- 从队列中取出已完全匹配的位置,尝试更新其它未处理完的起始位置。
- 对于未完成的起始位置,检查当前队列元素是否可以消除一些不匹配点。
- 如果通过匹配更新后,某起始位置变为完全匹配,加入队列并更新
ans
。
-
完成检查与输出:
- 完成后,检查是否所有位置都已被处理,如果是,则输出逆序的
ans
数组。 - 如果存在未处理完的位置,则返回空数组表示无法完成覆盖。
- 完成后,检查是否所有位置都已被处理,如果是,则输出逆序的
代码逻辑的重点
- 集合的动态更新:在处理过程中,匹配集和不匹配集的动态更新是算法的核心,确保了每次操作都是基于最新的匹配状态。
- 广度优先搜索的运用:通过使用 BFS 来处理所有完全匹配的起始位置,从而实现了有效的覆盖操作扩散。
- 逆序输出:题目要求逆序输出操作步骤,这是因为我们是从后向前确认每个位置最终的覆盖状态。
代码实现
class Solution {
public:
vector<int> movesToStamp(string stamp, string target) {
int m = stamp.length(), n = target.length();
vector<pair<unordered_set<int>, unordered_set<int>>> a;
vector<bool> adone(n - m + 1, true);
queue<pair<unordered_set<int>, unordered_set<int>>> q;
vector<int> ans;
for (int i = 0; i <= n - m; i++) {
unordered_set<int> matched, unmatched;
for (int j = 0; j < m; j++) {
if (stamp[j] == target[i + j]) {
matched.insert(i + j);
} else {
adone[i] = false;
unmatched.insert(i + j);
}
}
a.emplace_back(make_pair(matched, unmatched));
if (adone[i]) {
q.push(make_pair(matched, unmatched));
ans.push_back(i);
}
}
while (!q.empty()) {
auto u = q.front();
q.pop();
for (int i = 0; i <= n - m; i++) {
if (adone[i]) {
continue;
}
auto &[m, um] = a[i];
for (auto j: u.first) {
if (um.count(j)) {
um.erase(j);
m.insert(j);
}
}
if (um.empty()) {
q.push(make_pair(m, um));
adone[i] = true;
ans.push_back(i);
}
}
}
bool done = true;
for (auto d: adone) {
if (!d) {
done = false;
}
}
if (done) {
reverse(ans.begin(), ans.end());
return ans;
}
return {};
}
};