LeetCode 940 解題思路

這道題是隨機選的,看了看題目,剛好和前段時間看了MIT6.00課程中的Dynamic Programming很像,於是就想着去解決試試。沒找到Dynamic Programming在這裏的切入點,運算結果是對的(報:Time Limit Exceeded),就是計算複雜度太高了——O(2^{n}),當然十分慘烈。

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".

1. 直接求解

 首先用的是課程中介紹的,結合遞歸方法和二叉樹,對所有結果進行遍歷(窮舉法,又一次用上了Brute Force)。往樹的左邊不選當前字符,往樹的右邊就選一個字符,一直遞歸下去就可以獲得所有的結果。結果集中包括空字符和重複的,因此選用Set進行過濾,得到不重複的結果,可以時間複雜度太高,在LeetCode中的時候計算超時了。

sub_set = set()
def get_sub_set(left_str, S):
    if len(S) == 0:
        print left_str
        sub_set.add(left_str)
        return

    # print S
    not_pick_one = left_str
    get_sub_set(not_pick_one, S[1:])

    # print 'notPick', pick_left    
    pick_one = left_str + S[0]
    # print 'pick', pick_one
    get_sub_set(pick_one, S[1:])  

def distinctSubseqII(S):
    """
    :type S: str
    :rtype: int
    """
    get_sub_set('', S)
    return len(sub_set) - 1

def test():
    s1 = 'abc'
    print (7, distinctSubseqII(s1))

    s1 = 'aba'
    print (6, distinctSubseqII(s1))

    s1 = 'aaa'
    print (3, distinctSubseqII(s1))

test()

2. Dynamic Programming

昨天想了很久,沒想明白,睡覺的時候突然想起來,爲什麼結合前面二叉樹的分佈,來進行可視化操作呢?於是,在又經歷了近2小時,總於想透了,如下圖所示。

---

這裏有兩個假設

①在生成當前層字符時,所有來自上一層的字符都放在節點的左側。

②在生成當前層字符時,新生成的字符放在節點的右側。因此只需考慮新節點是否和之前的子字符重合即可。

我們需獲取字符‘aaa’的所有子字符串,按以下步驟

  1. index=0時,爲——空:\varnothing
  2. index=1時,爲——\varnothing,a
  3. index=2時,爲——\varnothing,a,a,aa。但由於以前的經驗(當前字符都是在以前字符的基礎上生成),我們知道從index=0的\varnothing中派生,會有結果\varnothing,a。而index=1中,也有一個\varnothing,並要進行向右派生。但此時不就和index=0中的情形重合了嗎?於是要刪去。最終結果爲:\varnothing,a,aa
  4. index=3時,同理,可以過濾掉重複的結果a,aa,得到\varnothing,a,aa,aaa

當我們要去求‘aaaa’的所有子字符串呢?以上步驟1-4不變,來看第五步,index=3的結果,直接繼承到樹的左側,現在只關注即將生成新結果的樹右側。本來index=3的結果產生來自於index=2,因此只需要去除此部分達到了去重的效果。

若在字符中混入了不同了字符,則判別方法類似,只需要找到同一個字符在上一個位置,並去除重複的數量即可——這裏包含的思想其實是分而治之。

解決方法如下,解決方法來自這個Discusss: 

def distinctSubseqII(S):
    """
    :type S: str
    :rtype: int
    """
    memo = {S[0]: 0}
    dp = [0] + [0]*len(S)
    dp[0] = 1
    dp[1] = 2
    mode_val = 10**9+7

    for index in range(1, len(S)):
        word = S[index]
        # print index, 'word:', word
        if word not in memo:
            dp[index+1] = (2 * dp[index] % mode_val)
        else:
            # print memo[word]
            value = 2 * dp[index] - dp[memo[word]]
            dp[index+1] = value % mode_val
        memo[word] = index
    # print dp
    return dp[-1] - 1

def test():
    s1 = 'abc'
    print (7, distinctSubseqII(s1))

    s1 = 'aba'
    print (6, distinctSubseqII(s1))

    s1 = 'aaa'
    print (3, distinctSubseqII(s1))

test()

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章