• Leetcode 115 Distinct Subsequences 解题报告


    Distinct Subsequences

     Total Accepted: 38466 Total Submissions: 143567My Submissions

    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

     

  • 相关阅读:
    PostgreSQL数据库管理:定期vacuum
    关于压力测试的思路
    作业自动提示功能设计思路
    This system is not registered with RHN
    读《高性能建站指南》(上)
    Ubuntu下PostgreSQL数据库集群(PL/Proxy)配置方法
    PG SQL数据库读写分离的思路
    ASP.NET知识点的明晰(非原创,东拼西凑的,向这些内容的原创者致敬)
    [转]Effective C#原则4:用条件属性而不是#if
    如何在Word文档中插入一条分隔线?
  • 原文地址:https://www.cnblogs.com/ArsenalfanInECNU/p/4793958.html
Copyright © 2020-2023  润新知