戳印序列

题目描述

你想要用小写字母组成一个目标字符串 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. 1 <= stamp.length <= target.length <= 1000
  2. stamp 和 target 只包含小写字母。

解题思路

  1. 初始化变量

    • mn 分别代表 stamptarget 的长度。
    • a 存储每个可能覆盖起始位置的匹配集(matched)和不匹配集(unmatched)。
    • adone 是一个布尔数组,标记每个起始位置是否已经处理完毕。
    • q 是一个队列,用于存放已完全匹配的起始位置,以进行广度优先搜索(BFS)。
  2. 遍历target构建匹配集合

    • target 进行遍历,对每个可能的起始位置检查 stamp 是否匹配。
    • 对于每个位置,遍历 stamp 的每个字符,构建匹配集和不匹配集。
    • 如果某个起始位置完全匹配(不匹配集为空),则加入队列并记录在 ans 中。
  3. 广度优先搜索处理完全匹配

    • 从队列中取出已完全匹配的位置,尝试更新其它未处理完的起始位置。
    • 对于未完成的起始位置,检查当前队列元素是否可以消除一些不匹配点。
    • 如果通过匹配更新后,某起始位置变为完全匹配,加入队列并更新 ans
  4. 完成检查与输出

    • 完成后,检查是否所有位置都已被处理,如果是,则输出逆序的 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 {};
    }
};

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LIHAORAN99

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值