你想要用小写字母组成一个目标字符串 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
只包含小写字母。解题思路
我们首先想到的最简单的方法是通过倒退回到初始点。对于第一个例子的target
,如果我们退回一步的话就变成了ab???
,接着再退回一步就变成了?????
(也就是初始状态)。
我们只要倒退的过程中记录stamp
在target
中的位置即可。依照这个思路去做的话,我们上来会碰到的问题就是如何替换stamp为?。一个非常简单的思路就是从头到尾的遍历和target
找可以和stamp
匹配的位置(?
可以和所有字符匹配),使用的方法是最简单的逐个字符比较,如果匹配成功,我们还要检查target
是不是都是?
,如果都是的话,那么显然我们已经有结果了,不需要继续下去了。
我们还有一个边界情况需要考虑,也就是如果我们从头至尾没有匹配到任何的stamp
的话,我们应该返回list()
。
class Solution:
def movesToStamp(self, stamp: str, target: str) -> List[int]:
t_len, s_len, target = len(target), len(stamp), list(target)
def check(i):
res = False
for a, b in zip(target[i:i + s_len], stamp):
if a != '?':
if a != b:
return False
res = True
return res
done, moves = ['?']*t_len, []
while target != done:
move = False
for i in range(t_len - s_len + 1):
if check(i):
move = True
moves.append(i)
target[i:i + s_len] = done[:s_len]
if not move:
return []
return moves[::-1]
这个问题显然还可以通过递归去解决。例如:
target: a b a b c #
i
stamp: a b c #
j
当我们发现target[i] == stamp[j]
的时候,此时我们首先看target[i+1]
和stamp[j+1]
是不是匹配,如果不可行(也就是没有解),那么需要在i+1
的地方记录一个戳印。例如:
target: a b a b c #
i
stamp: a b c #
j
此时target[i] != stamp[j]
,我们需要在i
的位置盖上一个戳印。
考虑边界问题,当i==len(target)
并且j==len(stamp)
的时候,此时说明有解,返回结果即可,否则返回空。当j==len(stamp)
,但i!=len(target)
的时候,表示匹配一个stamp
,例如:
target: a b c b c #
i
stamp: a b c #
j
此时,我们应该重新去匹配stamp
,但是j
的起始位置可以有k=range(len(stamp))
个选择,并且对应的戳印位置为i-k
。例如,如果此时j
从1
起始,那么i-1
的位置应该有一个戳印。
target: a b c b c #
i
stamp: a b c #
j
最后代码如下:
class Solution:
def movesToStamp(self, stamp: str, target: str) -> List[int]:
t_len, s_len = len(target), len(stamp)
mem = dict()
def dfs(i, j, paths):
if (i, j) in mem:
return mem[(i, j)]
if i == t_len:
return paths if j == s_len else []
res = []
if j == s_len:
for k in range(s_len):
res = dfs(i, k, [i - k] + paths)
if res:
break
elif target[i] == stamp[j]:
res = dfs(i + 1, j + 1, paths) or dfs(i + 1, 0, paths + [i + 1])
mem[(i, j)] = res
return res
return dfs(0, 0, [0])
reference:
https://leetcode.com/problems/stamping-the-sequence/discuss/189502/C++Python-12ms-Reverse-and-Greedy-O(N2M)
我将该问题的其他语言版本添加到了我的GitHub Leetcode
如有问题,希望大家指出!!!