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]
posted on 2016-05-25 20:31 Sheryl Wang 阅读(161) 评论(0) 编辑 收藏 举报