【三次过】Lintcode 118. 不同的子序列

给出字符串S和字符串T,计算S的不同的子序列中T出现的个数。

子序列字符串是原始字符串通过删除一些(或零个)产生的一个新的字符串,并且对剩下的字符的相对位置没有影响。(比如,“ACE”“ABCDE”的子序列字符串,而“AEC”不是)。 

样例

给出S = "rabbbit", T = "rabbit"

返回 3

挑战

Do it in O(n2) time and O(n) memory.

O(n2) memory is also acceptable if you do not know how to optimize memory.


解题思路:

看到有关字符串的子序列或者配准类的问题,首先应该考虑的就是用动态规划Dynamic Programming来求解,这个应成为条件反射。而所有DP问题的核心就是找出递推公式,想这道题就是递推一个二维的dp数组,下面我们从题目中给的例子来分析,这个二维dp数组应为:

  Ø r a b b b i t
Ø 1 1 1 1 1 1 1 1
r 0 1 1 1 1 1 1 1
a 0 0 1 1 1 1 1 1
b 0 0 0 1 2 3 3 3
b 0 0 0 0 1 3 3 3
i 0 0 0 0 0 0 3 3
t 0 0 0 0 0 0 0 3 

首先,若原字符串不为空,而子序列为空,返回1,因为空串也是任意字符串的一个子序列。

我们通过观察上面的二维数组可以发现,当更新到dp[i][j]时,dp[i][j] >= dp[i][j - 1] 总是成立,再进一步观察发现,当 T[i - 1] == S[j - 1] 时,dp[i][j] = dp[i][j - 1] + dp[i - 1][j - 1],若不等, dp[i][j] = dp[i][j - 1]

public class Solution {
    /**
     * @param S: A string
     * @param T: A string
     * @return: Count the number of distinct subsequences
     */
    public int numDistinct(String S, String T) {
        // write your code here
        if (S == null || T == null) 
            return 0;
        
        // dp[i][j]代表 t.substring(0 ,j)在s.substring(0 , i) 出现的次数
        int[][] dp = new int[S.length() + 1][T.length() + 1];

        //若原字符串不为空,而子序列为空,返回1,因为空串也是任意字符串的一个子序列。
        for (int i = 0; i <= S.length(); i++) 
            dp[i][0] = 1;
        
        for (int i = 1; i <= S.length(); i++) {
            for (int j = 1; j <= T.length(); j++) {
                if (S.charAt(i - 1) == T.charAt(j - 1)) 
                    dp[i][j] += dp[i - 1][j - 1] + dp[i - 1][j];
                else
                    dp[i][j] += dp[i - 1][j];
            }
        }
        
        return dp[S.length()][T.length()];
    }
}

 

你可能感兴趣的:(lintcode,动态规划)