天天看點

leetcode: 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

.

原問題連結:https://leetcode.com/problems/distinct-subsequences/

問題分析

  這個問題相對來說比較難找到思路。對于給定的源串s來說,要讓它的子串能夠構成目标串t,那麼理論上可能是它裡面的任何一個元素可能存在或者删除。如果按照這個思路,假設源串長度為m的話,那麼對它裡面每種情況進行構造并和目标串比較的可能性有2 ** m次。這種情況将使得解決方法不可接受。是以,我們需要根據原來的問題描述來尋找一個它們關系推導的規律。

  假設對于s來說,它的目前位置為i,對于t來說它的目前位置為j。那麼當s.charAt(i)  != t.charAt(j)的時候,說明s串裡i這個位置的元素必須被删除才行,否則沒法比對。這個時候它們的可能構成數量和s[i - 1], t[j]的值相同。那麼,當s.charAt(i) == t.charAt(j)的話呢?那麼s[i - 1], t[j - 1]的情況是一種選擇。而s[i - 1], t[j]也是一種選項。這個地方有點難以了解,為什麼s[i - 1], t[j]也是一種選項呢?因為我們在前面的問題定義裡,對于s來說它裡面每個元素可能存在也可能删除。那麼當删除第i個元素的時候,也有可能它前面的i - 1個元素和t中間的j個元素也構成了符合條件的序列,是以這種情況也應該包含進來。

  按照這個思路,我們可以定義一個二維矩陣int[][] matrix,它是一個(m + 1) x (n + 1)的矩陣。在最開始的時候,其中m, n分别表示s和t的長度。matrix[][]裡有一個初始條件,就是matrix[i][0] = 1。就是說,對于任意長度的源串s,對應于一個空串t來說,它都有一個子串,也就是删除它所有的字元。而對于其他的情況,則按照我們前面讨論的針對s.charAt(i - 1) == t.charAt(j - 1)的情況來處理。

  于是我們可以得到如下的代碼實作:

public class Solution {
    public int numDistinct(String s, String t) {
        int m = s.length();
        int n = t.length();
        if(n > m) return 0;
        int[][] matrix = new int[m + 1][n + 1];
        for(int i = 0; i <= m; i++) matrix[i][0] = 1;
        for(int i = 1; i <= m; i++) {
            for(int j = 1; j <= n; j++) {
                matrix[i][j] = matrix[i - 1][j] + (s.charAt(i - 1) == t.charAt(j - 1) ? matrix[i - 1][j - 1] : 0);
            }
        }
        return matrix[m][n];
    }
}
           

  這種實作的時間複雜度和空間複雜度基本上都是O(m * n) 。

改進

  在上述的代碼實作中,我們還有一個可以改進的細節,就是對于空間的使用。前面的二維數組可以縮小為一維的數組,因為我們這裡隻需要保留t裡面對應每個長度的比對個數,我們可以重用之前的結果。

  這種方式實作的代碼如下:

public class Solution {
    public int numDistinct(String s, String t) {
        int m = t.length();
        int n = s.length();
        if(m > n) return 0;
        int[] path = new int[m + 1];
        path[0] = 1;
        for(int j = 1; j <= n; j++) {
            for(int i = m; i >= 1; i--) {
                path[i] = path[i] + (t.charAt(i - 1) == s.charAt(j - 1) ? path[i - 1] : 0);
            }
        }
        return path[m];
    }
}