comments | difficulty | edit_url | tags | ||||
---|---|---|---|---|---|---|---|
true |
困难 |
|
你想要用小写字母组成一个目标字符串 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
只包含小写字母。
如果我们正向地对序列进行操作,那么处理起来会比较麻烦,因为后续的操作会把前面的操作覆盖掉。我们不妨考虑逆向地对序列进行操作,即从目标字符串
我们不妨记字母印章的长度为
首先,我们明确,每个开始位置都对应着一个长度为
接下来,我们定义以下数据结构或变量,其中:
- 入度数组
$indeg$ ,其中$indeg[i]$ 表示第$i$ 个窗口中有多少位置的字符与字母印章中的字符不同,初始时,$indeg[i]=m$。若$indeg[i]=0$ ,说明第$i$ 个窗口中的字符都与字母印章中的字符相同,那么我们就可以在第$i$ 个窗口中放置字母印章。 - 邻接表
$g$ ,其中$g[i]$ 表示目标字符串$target$ 的第$i$ 个位置上,所有与字母印章存在不同字符的窗口的集合。 - 队列
$q$ ,用于存储所有入度为$0$ 的窗口的编号。 - 数组
$vis$ ,用于标记目标字符串$target$ 的每个位置是否已经被覆盖。 - 数组
$ans$ ,用于存储答案。
接下来,我们进行拓扑排序。在拓扑排序的每一步中,我们取出队首的窗口编号
在拓扑排序结束后,如果目标字符串
时间复杂度
class Solution:
def movesToStamp(self, stamp: str, target: str) -> List[int]:
m, n = len(stamp), len(target)
indeg = [m] * (n - m + 1)
q = deque()
g = [[] for _ in range(n)]
for i in range(n - m + 1):
for j, c in enumerate(stamp):
if target[i + j] == c:
indeg[i] -= 1
if indeg[i] == 0:
q.append(i)
else:
g[i + j].append(i)
ans = []
vis = [False] * n
while q:
i = q.popleft()
ans.append(i)
for j in range(m):
if not vis[i + j]:
vis[i + j] = True
for k in g[i + j]:
indeg[k] -= 1
if indeg[k] == 0:
q.append(k)
return ans[::-1] if all(vis) else []
class Solution {
public int[] movesToStamp(String stamp, String target) {
int m = stamp.length(), n = target.length();
int[] indeg = new int[n - m + 1];
Arrays.fill(indeg, m);
List<Integer>[] g = new List[n];
Arrays.setAll(g, i -> new ArrayList<>());
Deque<Integer> q = new ArrayDeque<>();
for (int i = 0; i < n - m + 1; ++i) {
for (int j = 0; j < m; ++j) {
if (target.charAt(i + j) == stamp.charAt(j)) {
if (--indeg[i] == 0) {
q.offer(i);
}
} else {
g[i + j].add(i);
}
}
}
List<Integer> ans = new ArrayList<>();
boolean[] vis = new boolean[n];
while (!q.isEmpty()) {
int i = q.poll();
ans.add(i);
for (int j = 0; j < m; ++j) {
if (!vis[i + j]) {
vis[i + j] = true;
for (int k : g[i + j]) {
if (--indeg[k] == 0) {
q.offer(k);
}
}
}
}
}
for (int i = 0; i < n; ++i) {
if (!vis[i]) {
return new int[0];
}
}
Collections.reverse(ans);
return ans.stream().mapToInt(Integer::intValue).toArray();
}
}
class Solution {
public:
vector<int> movesToStamp(string stamp, string target) {
int m = stamp.size(), n = target.size();
vector<int> indeg(n - m + 1, m);
vector<int> g[n];
queue<int> q;
for (int i = 0; i < n - m + 1; ++i) {
for (int j = 0; j < m; ++j) {
if (target[i + j] == stamp[j]) {
if (--indeg[i] == 0) {
q.push(i);
}
} else {
g[i + j].push_back(i);
}
}
}
vector<int> ans;
vector<bool> vis(n);
while (q.size()) {
int i = q.front();
q.pop();
ans.push_back(i);
for (int j = 0; j < m; ++j) {
if (!vis[i + j]) {
vis[i + j] = true;
for (int k : g[i + j]) {
if (--indeg[k] == 0) {
q.push(k);
}
}
}
}
}
for (int i = 0; i < n; ++i) {
if (!vis[i]) {
return {};
}
}
reverse(ans.begin(), ans.end());
return ans;
}
};
func movesToStamp(stamp string, target string) (ans []int) {
m, n := len(stamp), len(target)
indeg := make([]int, n-m+1)
for i := range indeg {
indeg[i] = m
}
g := make([][]int, n)
q := []int{}
for i := 0; i < n-m+1; i++ {
for j := range stamp {
if target[i+j] == stamp[j] {
indeg[i]--
if indeg[i] == 0 {
q = append(q, i)
}
} else {
g[i+j] = append(g[i+j], i)
}
}
}
vis := make([]bool, n)
for len(q) > 0 {
i := q[0]
q = q[1:]
ans = append(ans, i)
for j := range stamp {
if !vis[i+j] {
vis[i+j] = true
for _, k := range g[i+j] {
indeg[k]--
if indeg[k] == 0 {
q = append(q, k)
}
}
}
}
}
for _, v := range vis {
if !v {
return []int{}
}
}
for i, j := 0, len(ans)-1; i < j; i, j = i+1, j-1 {
ans[i], ans[j] = ans[j], ans[i]
}
return
}
function movesToStamp(stamp: string, target: string): number[] {
const m: number = stamp.length;
const n: number = target.length;
const indeg: number[] = Array(n - m + 1).fill(m);
const g: number[][] = Array.from({ length: n }, () => []);
const q: number[] = [];
for (let i = 0; i < n - m + 1; ++i) {
for (let j = 0; j < m; ++j) {
if (target[i + j] === stamp[j]) {
if (--indeg[i] === 0) {
q.push(i);
}
} else {
g[i + j].push(i);
}
}
}
const ans: number[] = [];
const vis: boolean[] = Array(n).fill(false);
while (q.length) {
const i: number = q.shift()!;
ans.push(i);
for (let j = 0; j < m; ++j) {
if (!vis[i + j]) {
vis[i + j] = true;
for (const k of g[i + j]) {
if (--indeg[k] === 0) {
q.push(k);
}
}
}
}
}
if (!vis.every(v => v)) {
return [];
}
ans.reverse();
return ans;
}
use std::collections::VecDeque;
impl Solution {
pub fn moves_to_stamp(stamp: String, target: String) -> Vec<i32> {
let m = stamp.len();
let n = target.len();
let mut indeg: Vec<usize> = vec![m; n - m + 1];
let mut g: Vec<Vec<usize>> = vec![Vec::new(); n];
let mut q: VecDeque<usize> = VecDeque::new();
for i in 0..n - m + 1 {
for j in 0..m {
if target.chars().nth(i + j).unwrap() == stamp.chars().nth(j).unwrap() {
indeg[i] -= 1;
if indeg[i] == 0 {
q.push_back(i);
}
} else {
g[i + j].push(i);
}
}
}
let mut ans: Vec<i32> = Vec::new();
let mut vis: Vec<bool> = vec![false; n];
while let Some(i) = q.pop_front() {
ans.push(i as i32);
for j in 0..m {
if !vis[i + j] {
vis[i + j] = true;
for &k in g[i + j].iter() {
indeg[k] -= 1;
if indeg[k] == 0 {
q.push_back(k);
}
}
}
}
}
if vis.iter().all(|&v| v) {
ans.reverse();
ans
} else {
Vec::new()
}
}
}