我们对每个窗口维护两个集合 made 和 todo,前者表示和 stamp 可以匹配的位置,后者表示不可以匹配的位置(后者中只有某个位置的字符变成了问号,它才会变成可以匹配的位置)。只有当一个窗口的 todo 集合为空,这个窗口才可以被戳印,从而把一些字符变成问号。
我们用一个队列存储所有因为戳印而变成问号的字符位置。队列初始时包含所有 todo 集合一开始就为空的窗口对应的位置。当我们取出队列中的一个位置时,我们遍历所有覆盖了该位置的窗口,并且更新这些窗口的 todo 集合。如果 todo 集合变为空,那就说明产生了一个新的可被戳印的窗口,我们把这个窗口中所有未变成问号的字符的位置添加入队列中。
classSolution { publicint[] movesToStamp(String stamp, String target) { intM= stamp.length(), N = target.length(); Queue<Integer> queue = newArrayDeque(); boolean[] done = newboolean[N]; Stack<Integer> ans = newStack(); List<Node> A = newArrayList();
for (inti=0; i <= N-M; ++i) { // For each window [i, i+M), A[i] will contain // info on what needs to change before we can // reverse stamp at this window.
Set<Integer> made = newHashSet(); Set<Integer> todo = newHashSet(); for (intj=0; j < M; ++j) { if (target.charAt(i+j) == stamp.charAt(j)) made.add(i+j); else todo.add(i+j); }
A.add(newNode(made, todo));
// If we can reverse stamp at i immediately, // enqueue letters from this window. if (todo.isEmpty()) { ans.push(i); for (intj= i; j < i + M; ++j) if (!done[j]) { queue.add(j); done[j] = true; } } }
// For each enqueued letter (position), while (!queue.isEmpty()) { inti= queue.poll();
// For each window that is potentially affected, // j: start of window for (intj= Math.max(0, i-M+1); j <= Math.min(N-M, i); ++j) { if (A.get(j).todo.contains(i)) { // This window is affected A.get(j).todo.remove(i); if (A.get(j).todo.isEmpty()) { ans.push(j); for (int m: A.get(j).made) if (!done[m]) { queue.add(m); done[m] = true; } } } } }
for (boolean b: done) if (!b) returnnewint[0];
int[] ret = newint[ans.size()]; intt=0; while (!ans.isEmpty()) ret[t++] = ans.pop();
return ret; } }
classNode { Set<Integer> made, todo; Node(Set<Integer> m, Set<Integer> t) { made = m; todo = t; } }
classSolution(object): defmovesToStamp(self, stamp, target): M, N = len(stamp), len(target)
queue = collections.deque() done = [False] * N ans = [] A = [] for i in xrange(N - M + 1): # For each window [i, i+M), # A[i] will contain info on what needs to change # before we can reverse stamp at i.
made, todo = set(), set() for j, c inenumerate(stamp): a = target[i+j] if a == c: made.add(i+j) else: todo.add(i+j) A.append((made, todo))
# If we can reverse stamp at i immediately, # enqueue letters from this window. ifnot todo: ans.append(i) for j in xrange(i, i + len(stamp)): ifnot done[j]: queue.append(j) done[j] = True
# For each enqueued letter, while queue: i = queue.popleft()
# For each window that is potentially affected, # j: start of window for j in xrange(max(0, i-M+1), min(N-M, i)+1): if i in A[j][1]: # This window is affected A[j][1].discard(i) # Remove it from todo list of this window ifnot A[j][1]: # Todo list of this window is empty ans.append(j) for m in A[j][0]: # For each letter to potentially enqueue, ifnot done[m]: queue.append(m) done[m] = True
return ans[::-1] ifall(done) else []
复杂度分析
时间复杂度:O(N(N-M)),其中 M 和 N 分别是数组 stamp 和 target 的长度。