zoukankan      html  css  js  c++  java
  • [LeetCode#115]Distinct Subsequences

    Problem:

    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.

    Analysis:

    This problem is another type of dynamic programming problem.
    It's hard to understand, but it is really really powerful!!! You must master it!
    The simple solution also includes many awesome programming skills. You must learn it.
    
    Basic idea:
    We use a two dimensional matrix checkboard, checkboard[i][j] means how many sequences t[0, j] in s[0, i].
    Thus we could have following transitional equation.
    1. if s[i] != t[j], checkboard[i][j] = checkboard[i-1][j].
    Reason: the introduced s[i] means nothing for the number of sequences(which could be deleted!). Thus the subquences' number are the same for checkboard[i][j] and checkboard[i-1][j].
    
    2. if s[i] == t[j], checkboard[i][j] = checkboard[i-1][j] + checkboard[i-1][j-1].
    Reason:
    benefit 1 (not remove character): the s[i] and t[j] would not affect s[0, i-1] and t[0, j-1], the extra character at both string, would not affect subquence number checkboard[i-1][j-1].
    benefit 2 (remove charcter): Since we were allowed to remove characters from string s, the s[0, i] and t[0, j] still hold the subsequence from checkboard[i-1][j].
    
    Note: 
    1. checkboard[i-1] [j-1] has nothing to do with checkboard[i-1][j]
    case:
    s: aaab
    t: ab
    i = 3
    j = 1
    a. checkboard[i-1] [j-1] = 3
    s[0, i-1] = aaa
    t[0, 0] = a
    
    b. checkboard[i-1][j] = 0
    s[0, i-1] = aaa
    t[0, 1] = ab
    
    ************
    For this question, we care about how to delete characters from String s, thus to get String t.
    Therefore, the String t is the target we care about.
    1. checkboard[i][j] must not less than checkboard[i-1][j] ... checkboard[0][0], since we have more choice in deleting word, without comprising the probability to reach t.
    2. Only when s[i] == t[j], we could change ingore those two characters, and think over alone over s[0, i-1] and t[0, j-1]. 
    
    The additional two new characters do not reduce the subquences number from checkboard[i-1][j-1] (when the target still maintains).
    
    
    Even you have fully understood above realtionships. It is still hard to write the program right one time.
    wrong solution 1:
    int[] check_board = new int[t.length()];
    check_board[0] = (s.charAt(0) != t.charAt(0)) ? 0 : 1;
    for (int i = 1; i < s.length(); i++) {
        for (int j = 1; j < t.length(); j++) {
            check_board[j] = ((s.charAt(i) == t.charAt(j)) ? 0 : check_board[j-1]) + check_board[j]; 
        }
    }
    
    Big error:(wrong direction)
    Use one dimenional array to save space, but calculate through the wrong direction.
    When you try to do check_board[i-1][j-1] + check_board[i-1][j], the check_board[j-1] actually store check_board[i][j-1]
    It was rewritten by preivous step!!!!
    [i-1, j-1] [i-1][j]
                [i][j]
    Next time, when you encounter the situation of caculating current state through previous state, you must be very very careful.
    Even you figure out the problem, you may come into folowing solution(do calculation through backward)
    
    
    wrong solution 2:
    int[] check_board = new int[t.length()];
    check_board[0] = (s.charAt(0) != t.charAt(0)) ? 0 : 1;
    for (int i = 1; i < s.length(); i++) {
        for (int j = t.length() - 1; j > 0; j++) {
            check_board[j] = ((s.charAt(i) == t.charAt(j)) ? 0 : check_board[j-1]) + check_board[j]; 
        }
    }
    error 1: naively thinking checkboard[0] is the same for all checkborad[*][0]. Thus start from i = 1 and ignore checkboard[*][0]
    s: aaa
    t: a
    check board:
    1
    2
    3
    <checkboard[*][0] are different>
    Thus we must also update checkboard[*][0]
    
    However, if j = 0, the checkboard[j-i] would across the matrix's left boarder.
    A skill:
    Add a column on the left of the matrix, means no character in target string t. ""
    Thus the matrix becomes:
    1 0 0 0 0 0 0 0 0 0 0 0
    1 original matrix part
    1
    1
    1
    *********************************************************************************
    The whole column is '1', thus it requires no updates, we could stop at "column 1"
    **********************************************************************************
    
    This method is very tricky, but it holds the invarance for our problem, that checkboard[i][j] always indicate the subsequences s[0,i] , t[0,j] at here t is null.
    
    To implement it, first 
    int[] check_board = new int[t.length()+1]; (think it as a matrix of "t.length() + 1" columns)
    
    Then adjust the loop:
    for (int i = 0; i < s.length(); i++) {
        for (int j = t.length() - 1; j >= 0; j--) {
            check_board[j+1] = ((s.charAt(i) == t.charAt(j)) ? check_board[j] : 0) + check_board[j+1]; 
        }
    }
    return check_board[t.length()];
    
    Note: we just add new column before checkboard matrix.
    Since the the loop parameter is highly interwined with accessing each char in a String, it is better for us not to change the index for loop. But remember to place them into the right check_board position.
    
    For j in the loop, it actually means "j+1" in the index system of upgraded matrix.
    
    
    Pitfall, can we change 
    for (int i = 0; i < s.length(); i++) {}
    into 
    for (int i = 1; i < s.length(); i++) {}
    
    Absolutely no!!!
    We actually add a virtual column "" for t, and virtual row "" for s. 
    the first row for the matrix is (at beginning):
    1 0 0 0 0 ...
    
    If s.chartAt(0) = t.charAt(0), after the first iteration, we have
    1 0 0 0 0 0 ...
    1 1 0 0 0 0 ...
    
    Apparently the checkboard was updated, which could not be used for refering row = 0, then we start from row = 1 would incure problem.

    Solution:

    public class Solution {
        public int numDistinct(String s, String t) {
            if (s == null || s.length() < t.length())
                return 0;
            if (t == null || t.length() == 0)
                return 1;
            //must be careful with the index
            int[] check_board = new int[t.length()+1];
            check_board[0] = 1;
            for (int i = 0; i < s.length(); i++) {
                for (int j = t.length() - 1; j >= 0; j--) {
                    check_board[j+1] = ((s.charAt(i) == t.charAt(j)) ? check_board[j] : 0) + check_board[j+1]; 
                }
            }
            return check_board[t.length()];
        }
    }
  • 相关阅读:
    【机器学习】关联规则挖掘(二):频繁模式树FP-growth
    【机器学习】关联规则分析(一):Apriori
    【机器学习】聚类算法——K均值算法(k-means)
    【机器学习】分类器组合——AdaBoost
    tensorflow 中 Cross Entropy算法理解
    修改文件夹中的文件名
    poj 2635
    噪音样本
    流量录制回放助力接口自动化测试
    git
  • 原文地址:https://www.cnblogs.com/airwindow/p/4753742.html
Copyright © 2011-2022 走看看