940. Distinct Subsequences II
題目連結
題目描述
Given a string S, count the number of distinct, non-empty subsequences of S .
Since the result may be large, return the answer modulo 10^9 + 7.
Example 1: Input: "abc" Output: 7 Explanation: The 7 distinct subsequences are "a", "b", "c", "ab", "ac", "bc", and "abc". Example 2: Input: "aba" Output: 6 Explanation: The 6 distinct subsequences are "a", "b", "ab", "ba", "aa" and "aba". Example 3: Input: "aaa" Output: 3 Explanation: The 3 distinct subsequences are "a", "aa" and "aaa". Note: S contains only lowercase letters. 1 <= S.length <= 2000
這種題目其實可以類比subset
相關題目,但是該題目和subset題目有相似之處也有不同之處。
不同:subset
中的元素可以和原集合中的元素順序保持不一致,因此,我們在計算之前可以將原集合進行排序Subset, SubsetII。 但是該題目中,子集合中的元素順序必須和原本集合中的元素順序保持一致,因此,我們不能排序,排序之後再回溯也會超時。
相同: 都可以在之前求出結果的基礎上添加當前元素形成新的集合。注:之前subset這道題我使用的是回溯的方法,因此複雜度是o(2的n次方),但是subset這道題也能不使用回溯,而是使用在之前基礎上新增新元素的形式(藉助set)。
下面兩種方法本質上都是在之前結果的基礎上新增新元素的方式。
方法一
dp[i]
表示以S[i]
結尾的不同子字串的數量,則對應的方程為:
i的範圍為{0..len}, j的範圍為{0, i}
dp[i] += dp[j] s[i] != s[j]
dp[i] += 0 s[i]==s[j] //避免重複
以字串abb
為例,初始時,每個dp[i]
都為1
當i = 0
時,dp[0] = 1
(初始化值),其代表1個字串a
當i = 1
時,dp[1] += dp[0]
, 即dp[1]=2
,其代表字串ab, b
當i = 2
時, dp[2] += dp[0]
, 即dp[2] = 2
,其代表的字串為bb
和abb
(注:原本應該為dp[0] + dp[1]
ab, abb, bb, b
的,就是將之前算過的所有字串拼接上新的當前字元,但是因為存在重複的,例如dp[1]代表的是ab, b和當前算出4箇中就有2重複,因此,我們就不將和之前相等的字串結果進行相加了)
其實這個方法本質上下文的方法二是一樣的
class Solution {
public int distinctSubseqII(String S) {
if(S == null || S.length() == 0) {
return 0;
}
int mod = 1_000_000_007;
int[] dp = new int[S.length()];
Arrays.fill(dp, 1);
for(int i = 0; i < S.length(); i++) {
for(int j = 0; j < i; j++) {
if(S.charAt(i) != S.charAt(j)) {
dp[i] = (dp[i] + dp[j]) % mod;
}
}
}
int ans = 0;
for(int i = 0; i < S.length(); i++) {
ans = (ans + dp[i]) % mod;
}
return ans;
}
}
方法二
題目說只會有小寫字母出現,因此我們新建陣列endWithChar[26]
,其含義為:endWithChar[0]
表示以字母a
結尾的字串個數,endWithChar[1]
表示以字母b
結尾的字串的個數,依次類推。
我們從頭開始遍歷字串的每個字元,當前我們已經或者的字串個數為N = sum(endWithChar[0] + endWithChar[1] + ... + endWithChar[25])
,假設現在新來一個字元c
,則在之前字串的末尾加上c
之後,會有新的N
個字串以c
結尾,再加上1(表示單獨的一個c
字串),就是當前以c
結尾的字串的數量。下面是一個例子。
假設S = abb
,初始時,endWithChar
的每個元素都為0,下面是遍歷過程:
i = 0, S[i] = a ,
則endWithChar[a] = sum(endWithChar) + 1 = 1,
代表字串 a
i = 1, S[i] = b
, 則endWithChar[b] = sum(endWithChar) + 1 = 2
, 代表兩個字串ab
, b
i=2, S[i] =b,
則endWithChar[b] = sum(endWIthChar) + 1 = 1 + 2 + 1 = 4
,代表字串ab
, abb
, bb
, b
因此,最後結果為sum(endWithChar) = 1 + 4 = 5
,代表字串a, ab, abb, bb, b
class Solution {
public int distinctSubseqII(String S) {
if(S == null || S.length() == 0) {
return 0;
}
int mod = 1_000_000_007;
int[] endWithChar = new int[26];
int length = S.length();
for(int i = 0; i < length; i++) {
endWithChar[S.charAt(i) - 'a'] = (getSum(endWithChar, mod) + 1) % mod;
}
return getSum(endWithChar, mod);
}
private int getSum(int[] array, int mod) {
int temp = 0;
for(int j = 0; j < 26; j++) {
temp = (temp + array[j]) % mod;
}
return temp;
}
}