• Distinct Subsequences


    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.

    这题是求在S中有多少T的不同子串。这是一道典型的双序列动态规划问题。重点在于如何定义状态(也就是最优解的结构特征,一般表达形式)。这一题如果直接想拿整体的T在S中查找,无从下手。所以使用双序列问题的一般状态形式,即求子串一的第i 个和子串二的第j个关系。在这里f[i][j]是S的前i个字符中包含多少个不同子串是T的前j个字符。

    之后是转移方程。首先这种字符串的题目没有明确的可以跳跃的地方,所以考虑f[i][j]如何从[i-1][j-1],f[i][j-1],f[i-1][j]转换过来。首先是f[i][j-1]是无法转换到f[i][j]的因为无法搭建j-1到j的一个关系(因为本身是不连续的)。之后只剩下f[i-1][j-1]和f[i-1][j]。可以看到当S的第i个字符不等于T的第j个字符时(S[i-1]!=T[j-1]),这个多出来的字符没有用,f[i][j] = f[i-1][j]。而当S[i-1]==T[j-1]时,如果S的前i-1个字符中子串已经有T的前j-1个字符,则这i-1个字符和S的第i 个字符一下子可以组成同样个数的T的前j个字符。这些都是以S[i-1]结尾的,如果S的前i-1个字符中子串已经有T的前j个字符,这些又可以加进去(肯定是不同的子串,结尾的位置不一样)。

    状态:f[i][j] S的前i 个字符中有多少distinct subsequences是T的前j个字符。

    转移方程: f[i][j] = f[i-1][j] (s[i-1]!=t[j-1])

                   f[i][j] = f[i-1][j-1]+f[i-1][j](s[i-1]==t[j-1])

    初始状态和计算方向: f[i][0] =1,f[0][j]=0(j>0) 从上到下,从左到右

    最终答案为:f[m][n] 

    直接按照这种思路写出来的代码时间和空间都为O(mn)的解法如下,注意f[i][j]=0(i<j):

    class Solution(object):
        def numDistinct(self, s, t):
            """
            :type s: str
            :type t: str
            :rtype: int
            """
            if not t: 
                return 1
            if not s:
                return 0
            l1 = len(s)
            l2 = len(t)
            if l1 < l2:
                return 0
            res = [[0 for i in xrange(l2+1)] for j in xrange(l1+1)]
            for i in xrange(l1+1):
                res[i][0] = 1
                
            for j in xrange(1,l2+1):
                for i in xrange(j,l1+1):
                    if s[i-1] != t[j-1]:
                        res[i][j] = res[i-1][j]
                    else:
                        res[i][j] = res[i-1][j-1] + res[i-1][j]
            return res[l1][l2]

    当然和其他二维DP一样,这种解法也是可以进行空间优化的,可以使用滚动数组来一行一行处理。f[i-1][j-1]可以使用之前在Edit Distance中的策略使用一个pre来保存 f[i-1][j-1]。和Edit Distance那题不一样的是,这题不需要f[i][j-1]。所以完全可以从右往左计算,不需要使用pre保存中间结果。要快很多。

    从右往左的代码:

    class Solution(object):
        def numDistinct(self, s, t):
            """
            :type s: str
            :type t: str
            :rtype: int
            """
            if not t: 
                return 1
            if not s:
                return 0
            res = [0 for i in xrange(len(t)+1)]
            res[0] = 1  
            
            for i in xrange(1,len(s)+1):
                for j in xrange(len(t),0,-1):
                    if s[i-1] == t[j-1]:
                        res[j] += res[j-1]
            return res[-1]

    pre的代码,这种要慢一些:

    class Solution(object):
        def numDistinct(self, s, t):
            """
            :type s: str
            :type t: str
            :rtype: int
            """
            if not t: 
                return 1
            if not s:
                return 0
            res = [0 for i in xrange(len(t)+1)]
            res[0] = 1  
            
            for i in xrange(1,len(s)+1):
                pre = res[0]
                for j in xrange(1,len(t)+1):
                    tmp = res[j]        
                    if s[i-1] == t[j-1]:
                        res[j] += pre
                    pre = tmp
            return res[-1]
  • 相关阅读:
    安装MySQL Workbench 基于CentOS7在线安装MySQL Workbench
    记一次 ABP VNext 进程崩溃。错误源 System.ObjectDisposedException: Cannot access a disposed context instance.
    MySQL 8.0.27 Left Join 一个子查询的问题?
    Vue安装并使用axios
    VSCode安装React脚手架
    Vue 安装并使用nanoid
    VSCode使用Chrome调试Html,Js(支持跨域,访问本地Json)
    vfor指令
    AspNetPager 7.4.3版发布暨纪念AspNetPager发布十周年
    AspNetPager免费开源分页控件7.4.1版发布
  • 原文地址:https://www.cnblogs.com/sherylwang/p/5528403.html
Copyright © 2020-2023  润新知