In the Stamping The Sequence problem, you are given two strings: stamp and target, both consisting of lowercase English letters.
stamp string onto the target string at any position, as long as each character in stamp either matches the corresponding character in target or the corresponding character in target is already a question mark ('?').target at that position is replaced with '?'.target string into all question marks ('?') using the minimum number of stamping moves.Constraints:
1 <= stamp.length <= target.length <= 1000stamp and target consist of lowercase English letters.At first glance, it may seem natural to try stamping from left to right, greedily matching as many characters as possible. However, this can quickly run into situations where future required matches become impossible due to earlier moves.
Instead, consider the problem in reverse: what if we try to "unstamp" the target string back to all question marks? If we can repeatedly find substrings that could have been produced by stamping (i.e., they match the stamp or are already '?'), we can replace them with question marks, and keep track of the positions. Reversing the order of these positions gives us a valid stamping sequence.
This reverse approach is more robust because it avoids getting stuck due to premature stamping, and ensures that every move is valid with respect to the current state of the target.
The solution uses a greedy, reverse simulation. Here’s how we approach it step by step:
target into a mutable list for easy modification.'?'.target are '?':stamp could fit in target:'?' characters match stamp):'?'.
Why this works: By always stamping windows that can be fully erased (i.e., all their remaining letters match the stamp or are already '?'), we guarantee that every move is valid and that we eventually erase the entire target.
Input: stamp = "abc", target = "ababc"
ababcbab matches abc only if we ignore the first character. But at position 0: aba matches abc only in the first two letters.
abc matches abc exactly. We stamp at position 2, turning target into ab???.
ab? can be stamped, since ab? matches abc in the first two letters and the last is already '?'. We stamp at position 0, turning target into ?????.
O(N) iterations, where N is the length of target.M (length of stamp), so O(N * M) per iteration.O(N^2 * M) in the worst case.O(N) for storing the mutable target and the result positions.
The key insight for this problem is to work backwards, repeatedly "unstamping" any window that could have been produced by a valid stamping move. This ensures that every move is legal and avoids getting stuck. By tracking the positions and reversing the result, we obtain a valid stamping sequence that transforms target into all question marks. This approach is both efficient and elegant, leveraging greedy matching and careful state tracking.
class Solution:
def movesToStamp(self, stamp: str, target: str):
S, T = list(stamp), list(target)
slen, tlen = len(stamp), len(target)
res = []
visited = [False] * (tlen - slen + 1)
stars = 0
def can_stamp(pos):
changed = False
for i in range(slen):
if T[pos + i] == '?':
continue
if T[pos + i] != S[i]:
return False
changed = True
return changed
def do_stamp(pos):
nonlocal stars
for i in range(slen):
if T[pos + i] != '?':
T[pos + i] = '?'
stars += 1
while stars < tlen:
stamped = False
for i in range(tlen - slen + 1):
if not visited[i] and can_stamp(i):
do_stamp(i)
visited[i] = True
res.append(i)
stamped = True
if not stamped:
return []
return res[::-1]
class Solution {
public:
vector<int> movesToStamp(string stamp, string target) {
int slen = stamp.size(), tlen = target.size();
vector<char> T(target.begin(), target.end());
vector<bool> visited(tlen - slen + 1, false);
vector<int> res;
int stars = 0;
auto can_stamp = [&](int pos) {
bool changed = false;
for (int i = 0; i < slen; ++i) {
if (T[pos + i] == '?') continue;
if (T[pos + i] != stamp[i]) return false;
changed = true;
}
return changed;
};
auto do_stamp = [&](int pos) {
for (int i = 0; i < slen; ++i) {
if (T[pos + i] != '?') {
T[pos + i] = '?';
++stars;
}
}
};
while (stars < tlen) {
bool stamped = false;
for (int i = 0; i <= tlen - slen; ++i) {
if (!visited[i] && can_stamp(i)) {
do_stamp(i);
visited[i] = true;
res.push_back(i);
stamped = true;
}
}
if (!stamped) return {};
}
reverse(res.begin(), res.end());
return res;
}
};
class Solution {
public int[] movesToStamp(String stamp, String target) {
char[] S = stamp.toCharArray();
char[] T = target.toCharArray();
int slen = S.length, tlen = T.length;
boolean[] visited = new boolean[tlen - slen + 1];
List<Integer> res = new ArrayList<>();
int stars = 0;
while (stars < tlen) {
boolean stamped = false;
for (int i = 0; i <= tlen - slen; i++) {
if (!visited[i] && canStamp(T, i, S)) {
stars += doStamp(T, i, S.length);
visited[i] = true;
res.add(i);
stamped = true;
}
}
if (!stamped) return new int[0];
}
int[] ans = new int[res.size()];
for (int i = 0; i < res.size(); i++) {
ans[i] = res.get(res.size() - 1 - i);
}
return ans;
}
private boolean canStamp(char[] T, int pos, char[] S) {
boolean changed = false;
for (int i = 0; i < S.length; i++) {
if (T[pos + i] == '?') continue;
if (T[pos + i] != S[i]) return false;
changed = true;
}
return changed;
}
private int doStamp(char[] T, int pos, int slen) {
int count = 0;
for (int i = 0; i < slen; i++) {
if (T[pos + i] != '?') {
T[pos + i] = '?';
count++;
}
}
return count;
}
}
var movesToStamp = function(stamp, target) {
const S = stamp.split(''), T = target.split('');
const slen = S.length, tlen = T.length;
const visited = new Array(tlen - slen + 1).fill(false);
const res = [];
let stars = 0;
function canStamp(pos) {
let changed = false;
for (let i = 0; i < slen; i++) {
if (T[pos + i] === '?') continue;
if (T[pos + i] !== S[i]) return false;
changed = true;
}
return changed;
}
function doStamp(pos) {
for (let i = 0; i < slen; i++) {
if (T[pos + i] !== '?') {
T[pos + i] = '?';
stars++;
}
}
}
while (stars < tlen) {
let stamped = false;
for (let i = 0; i <= tlen - slen; i++) {
if (!visited[i] && canStamp(i)) {
doStamp(i);
visited[i] = true;
res.push(i);
stamped = true;
}
}
if (!stamped) return [];
}
return res.reverse();
};