【Leetcode】115. Distinct Subsequences

题目地址:

https://leetcode.com/problems/distinct-subsequences/

给定两个字符串 s s s t t t,问 t t t作为子序列在 s s s中出现了多少次。

法1:动态规划。设 f [ i ] [ j ] f[i][j] f[i][j] s [ 0 : i ] s[0:i] s[0:i] t [ 0 : j ] t[0:j] t[0:j]作为子序列出现了多少次。那么,如果 s [ i ] ≠ t [ j ] s[i]\ne t[j] s[i]=t[j],则子序列 t [ 0 : j ] t[0:j] t[0:j]只能是在 s [ 0 : i − 1 ] s[0:i-1] s[0:i1]里出现,所以 f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i-1][j] f[i][j]=f[i1][j];如果 s [ i ] = t [ j ] s[i]=t[j] s[i]=t[j],则除了 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j]之外, s [ i ] s[i] s[i]可以与 t [ j ] t[j] t[j]进行配对,再加上 s [ 0 : i − 1 ] s[0:i-1] s[0:i1] t [ 0 : j − 1 ] t[0:j-1] t[0:j1]作为子序列,合起来就能拼成 t [ 0 : j ] t[0:j] t[0:j],所以此时 f [ i ] [ j ] = f [ i − 1 ] [ j ] + f [ i − 1 ] [ j − 1 ] f[i][j]=f[i-1][j]+f[i-1][j-1] f[i][j]=f[i1][j]+f[i1][j1]。综上有: f [ i ] [ j ] = { f [ i − 1 ] [ j ] , s [ i ] ≠ t [ j ] f [ i − 1 ] [ j ] + f [ i − 1 ] [ j − 1 ] , s [ i ] = t [ j ] f[i][j]=\begin{cases}f[i-1][j], s[i]\ne t[j]\\ f[i-1][j]+f[i-1][j-1], s[i]=t[j]\end{cases} f[i][j]={ f[i1][j],s[i]=t[j]f[i1][j]+f[i1][j1],s[i]=t[j]边界条件,如果 s [ 0 ] = t [ 0 ] s[0]=t[0] s[0]=t[0]的话,则有 f [ 0 ] [ 0 ] = 1 f[0][0]=1 f[0][0]=1,否则 f [ 0 ] [ 0 ] = 0 f[0][0]=0 f[0][0]=0,而 f [ i ] [ 0 ] f[i][0] f[i][0]实际上就是 s [ 0 : i ] s[0:i] s[0:i]里字符 t [ 0 ] t[0] t[0]出现了多少次。容易看出 ∀ j > 0 , f [ 0 ] [ j ] = 0 \forall j > 0,f[0][j]=0 j>0,f[0][j]=0。最后要返回的就是 f [ l s − 1 ] [ l t − 1 ] f[l_s-1][l_t-1] f[ls1][lt1]。代码如下:

public class Solution {
     
    public int numDistinct(String s, String t) {
     
    	// 这里要特判一下空串
        if (s.isEmpty()) {
     
            return t.isEmpty() ? 1 : 0;
        }
        
        int[][] dp = new int[s.length()][t.length()];
        dp[0][0] = s.charAt(0) == t.charAt(0) ? 1 : 0;
        for (int i = 1; i < s.length(); i++) {
     
            dp[i][0] = dp[i - 1][0] + (s.charAt(i) == t.charAt(0) ? 1 : 0);
        }
        
        for (int i = 1; i < s.length(); i++) {
     
            for (int j = 1; j < t.length(); j++) {
     
                dp[i][j] = dp[i - 1][j];
                if (s.charAt(i) == t.charAt(j)) {
     
                    dp[i][j] += dp[i - 1][j - 1];
                }
            }
        }
        
        return dp[s.length() - 1][t.length() - 1];
    }
}

时空复杂度 O ( l s l t ) O(l_sl_t) O(lslt)

法2:记忆化搜索。设 f [ i ] [ j ] f[i][j] f[i][j] s [ i : ] s[i:] s[i:]包含 t [ j : ] t[j:] t[j:]子序列数目,则有: f [ i ] [ j ] = { f [ i + 1 ] [ j ] , s [ i ] ≠ t [ j ] f [ i + 1 ] [ j ] + f [ i + 1 ] [ j + 1 ] , s [ i ] = t [ j ] f[i][j]=\begin{cases}f[i+1][j],s[i]\ne t[j]\\ f[i+1][j]+f[i+1][j+1], s[i]=t[j]\end{cases} f[i][j]={ f[i+1][j],s[i]=t[j]f[i+1][j]+f[i+1][j+1],s[i]=t[j]递归出口是 f [ k ] [ l t ] f[k][l_t] f[k][lt],这是 s [ k : ] s[k:] s[k:]含多少个空串子序列,其实就是 1 1 1。代码如下:

import java.util.Arrays;

public class Solution {
     
    public int numDistinct(String s, String t) {
     
        int[][] dp = new int[s.length()][t.length()];
        for (int[] row : dp) {
     
            Arrays.fill(row, -1);
        }
        
        return dfs(0, 0, s, t, dp);
    }
    
    private int dfs(int i, int j, String s, String t, int[][] dp) {
     
    	// 如果后面长度不够了,则直接返回0
        if (s.length() - i < t.length() - j) {
     
            return 0;
        }
        
        // j走完t了,返回1(相当于s[i : ]含空串子序列数目,是1)
        if (j == t.length()) {
     
            return 1;
        }
        
        // 如果有记忆则调取记忆
        if (dp[i][j] != -1) {
     
            return dp[i][j];
        }
        
        int res = 0;
        for (int k = i; k < s.length(); k++) {
     
            res = dfs(i + 1, j, s, t, dp);
            if (s.charAt(i) == t.charAt(j)) {
     
                res += dfs(i + 1, j + 1, s, t, dp);
            }
        }
        
        // 返回之前存一下记忆
        dp[i][j] = res;
        return res;
    }
}

时空复杂度一样。

你可能感兴趣的:(#,贪心,动态规划与记忆化搜索,leetcode,dfs,字符串,动态规划,算法)