下面我们看两道字符串子序列的问题 subsequence,首先明白子序列是不必连续的。
Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2.
Example 1:
Input: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac" Output: true
题目解析:
先处理一些特殊情况。
解法参考https://leetcode.com/problems/interleaving-string/discuss/544593/Python-faster-than-97.40
首先理解dp变量的含义,在for循环中dp代表主串s3在当前位置i处s3[:i]的解决方案,所用的s1和s2的位置,比如3: {(1, 1), (2, 0)},当aadb时,有两种方案,或者是 aa + db ,或者是 aab + d。
状态转移来说,要根据dp[i-1]的解决方案和当前字符,来生成dp[i]的解决方案,看代码即可。
下面的代码做了简化,dp是一维的,dp中的元素,代表S1和S2的解决方案的位置,做成字符串,方便dp集合做哈希处理。
最后结果只要dp中有一种解决方案即可。
class Solution:
def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
la, lb = len(s1), len(s2)
lc = len(s3)
if la + lb != lc:
return False
if la == 0:
return s2 == s3
if lb == 0:
return s1 == s3
dp = set()
dp.add("0 0")
for i in range(lc):
char = s3[i]
dp_ = set()
for s in dp:
x, y = map(int, s.split(" "))
if x < la and s1[x] == char:
dp_.add("%d %d" % (x+1, y))
if y < lb and s2[y] == char:
dp_.add("%d %d" % (x, y+1))
dp = dp_
return len(dp) > 0
Given a string S and a string T, count the number of distinct subsequences of S which equals T.
A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ACE"
is a subsequence of "ABCDE"
while "AEC"
is not).
题目解析:
也是子序列的问题,鄙人写的代码如下,比较慢,DP的思想比较弱,属于常规思维。dp的第一维代表主串S的每个位置的信息,信息即为S串对应T串的解决方案,思路与上题类似。最终dp[i][-1]代表T串完成一个解决方案,累加即可。
class Solution:
def numDistinct(self, s: str, t: str) -> int:
ls = len(s)
lt = len(t)
if ls < lt:
return 0
if ls == lt:
return 1 if s == t else 0
dp = [[] for _ in range(ls)]
max_num = 0
t_dict = dict()
for ix, char in enumerate(t):
t_dict[char] = t_dict.get(char, [])
lst = t_dict[char]
lst.append(ix)
for i in range(ls):
char = s[i]
dp[i] = [0 for _ in range(lt)]
idx_lst = t_dict.get(char, [])
if len(idx_lst) == 0:
continue
for j in range(i):
lst = dp[j]
# lst代表s串每个位置的信息
for ix in idx_lst:
num = lst[ix-1]
# info代表与t串对应的位置索引和次数
if num == 0:
continue
dp[i][ix] += num
if char == t[0]:
dp[i][0] = 1
max_num += dp[i][-1]
return max_num
上面的方法太慢,800+ms。下面才是DP的思想。首先,建立T串的字典,字符及其索引的map;dp的长度为T的长度,然后我们要遍历S,看当前字符的索引位置,有的话则索引idx代表的子串t[:idx]的解决方案就等于t[:idx-1]的解决方案的次数,首字母直接+1。自己需要理解一下。另外注意索引的遍历要从大到小,原因可以rabbit为例看一下。
class Solution:
def numDistinct(self, s: str, t: str) -> int:
ls = len(s)
lt = len(t)
if ls < lt:
return 0
if ls == lt:
return 1 if s == t else 0
t_dict = dict()
for ix, char in enumerate(t):
t_dict[char] = t_dict.get(char, [])
lst = t_dict[char]
lst.append(ix)
dp = [0 for _ in range(lt)]
for char in s:
idx_lst = t_dict.get(char, [])
if len(idx_lst) == 0:
continue
for idx in idx_lst[::-1]:
if idx == 0:
dp[0] += 1
else:
dp[idx] += dp[idx-1]
return dp[-1]
字符串的DP问题就介绍这么几个很典型的题目了。