Leetcode 115 Distinct Subsequences 解题报告

Distinct Subsequences

Total Accepted: 38466 Total Submissions: 143567My Submissions

Question Solution 

Given a string S and a string T, count the number of distinct subsequences of T in S.

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).

Here is an example:
S = "rabbbit", T = "rabbit"

Return 3.

 
  首先要读懂题意。题目中“count the number of distinct subsequences of T in S.” sequence是序列的意思,subqequence就是子序列。我们知道序列是由一个个元素排列而成。所以,题目中的“字符串T”为“字符串S”的子序列。求“字符串S”中“子序列字符串T”的个数,也就变形为:取出“字符串S”中的若干元素,顺序排列,组成“字符串T”,有多少种取法?
  我认为上述分析思路比题目中提到的“删除元素”的思路要好。因为当T和S长度相近时,两种思路效率差不多;当S远大于T时,由于组成一次“字符串T”要删除非常多的元素,显然,效率远低于第一种思路。
 
  参考 Rachel Zhang 的解题报告,同样的,我具体用了两种不同的方法去解这个题。
 
  方法一:迭代+递归,具体思路是深度优先搜索(Depth First Search),但无法通过,因为TLE(Time Limit Exceeded)
  以字符串S=“PabZcdefSghZijZkSlmZnoPqrStuZvwSxZyZ” ,字符串T=“PSZ”为例。
  为了方便大家观察,我把'P'、'S'、'Z'在字符串S中的位置着重标出来。
  S中元素'P'的位置=0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
  S中元素'S'的位置=0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
  S中元素'Z'的位置=0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
  观察字符串。字符串中任意的元素'P'、'S'、'Z',如果以顺序排列,则组成满足条件的字符串S"PSZ"。
  那么我们如何保证在取'P'、'S'、'Z'的时候既是顺序排列,又不多取,不少取呢?
  显然,用DFS就可以做到。
  下面为不熟悉DFS的朋友具体讲个例子,高手可以直接跳过。
  Eg:
  先找字符串S中有几个'P',2个: 0, 23
  对位置为0的字符‘P’,其后面有几个'S',4个:8,16,25,31
  对位置为8的字符'S',其后面有几个'Z',6个:11,14,19,  28, 33, 35
  所以'0 8 11', '0 8 14 ', '0 8 19', '0 8 28', '0 8 28', '0 8 33', '0 8 35'都是subsequence“PSZ”。
  依次再遍历'0 16 X'、'0 25 X'的情况,依次类推。非常明显,这个例子就是“深度优先的”。
  以下是代码:
class Solution(object):
def numDistinct(self, s, t):
"""
:type s: str
:type t: str
:rtype: int
"""
num=[0]
self.CountSubsequence(s,t,0,0,num)
return num[0]
def CountSubsequence(self,father_sequence,child_sequence,index_father,index_child,num):
#print(index_father,index_child)
len_father=len(father_sequence)
len_child=len(child_sequence)
if index_child==len_child:
num[0]+=1
#print("匹配到了相同的")
else:
#print("进入迭代")
for i in range(index_father,len_father):
if father_sequence[i]==child_sequence[index_child]:
self.CountSubsequence(father_sequence,child_sequence,i+1,index_child+1,num)
#这里num是一个列表,可以从外部访问的,所以不需要return

  方法二:DP(Dynamic Programming, 动态规划)

  此处参考陆草纯的解题报告将问题转化为“二维地图走法问题”。

  我觉得他在文章里对转化为“二维地图走法问题”说明的不清楚:

  疑问一:为何走的时候只能“对角线走”和“向右向下走”,不能“向下向右走”。

  疑问二:为何字符判断相等时,是“对角线走”和“向右向下走”相加;而字符不等时,只能“向右向下走”。

  经过自己的思考,我来说一下我的理解:

  一个子字符串t',一个父字符串s',两者一点一点相加。最终子字符串的长度加到T的长度,父字符串的长度加到S的长度。

  当字符不等时,也就是说,父字符串s‘中新加的元素s'[i]无法对走法有贡献,所以可以删掉,于是就变成了“向右向下走”

  字符相等时,父字符串s'中新加的元素s'[i]对走法有贡献,所以对角线是可以取的;同时“向右向下走”(即删掉s'[i])也是可行的;由于两者是不同的走法,自然要相加。

  显然,DP的思路是从0开始一点一点增加子字符串的长度,最终达到我们想要匹配的字符串长度。显然不能减少字符串t'的长度。

  大家画个图就明白了,以s' 为纵轴,t'为横轴。下面直接上AC的python代码:

  

class Solution(object):
def numDistinct(self, s, t):
"""
:type s: str
:type t: str
:rtype: int
"""
#s is father_sequence
#t is child_sequence
len_father=len(s)
len_child=len(t)
dp=[[0 for i in range(len_child)] for j in range(len_father)]
if len_father==0 or len_child==0:
result=0
else:
#dp=[[0 for i in range(len_child)] for j in range(len_father)]
if s[0]==t[0]:
dp[0][0]=1
for i in range(1,len_father):
dp[i][0]=dp[i-1][0]
if s[i]==t[0]:
dp[i][0]+=1 for i in range(1,len_father):
for j in range(1,len_child):
if i>=j:
if s[i]==t[j]:
dp[i][j]=dp[i-1][j-1]+dp[i-1][j]
else:
dp[i][j]=dp[i-1][j]
result=dp[len_father-1][len_child-1]
return result
上一篇:gitlab4.0->5.0->6.0->7.14->8.0->8.2升级


下一篇:iOS js oc相互调用(JavaScriptCore)---js调用iOS --js里面通过对象调用方法