大家好,歡迎閱讀codeforces專題。
今天選擇的題目是Div3比賽的最後一題,也是最難的一道題。選這道題的主要原因是幫助大家建立信心,因為有些小夥伴給我反應說之前選擇的題目有些難了,覺得自己可能應付不了codeforces的題目。所以今天特地選了Div3比賽當中的最難題來給大家一點信心。
Div3比賽是難度最低的比賽,面向的是演算法的初學者,非常適合大家作為入門練習。由於是最後一道題,所以這題的通過人數不是很多,大概是530人。但是這題並不很難,我感覺大約和我們Div2中的C題相當。
連結:https://codeforces.com/contest/1426/problem/F
廢話不多說了,讓我們直接看題吧。
題意
給定一個只有abc和?的字串,其中的?可以被任意替換成a、b或者c。我們假設?一共有k個,這樣我們一共可以得到個不同的字串。
要求所有這些字串當中擁有的abc子序列的個數,子序列是指原串在不改變元素相對順序的情況下通過刪除元素得到的子集。比如說baacbc擁有兩個abc的子序列,分別是下標組合(2,5,6)和(3,5,6)。
由於最終的數量可能會很大,要求這個結果對取模。
樣例
輸入一共有兩行,第一行給定一個整數n(),表示字串的長度。第二行輸入長度為n的字串,要求返回abc子序列的個數。
對於第一個樣例的解釋:
題解
這題看起來比較複雜,想要直接想出解法來正面攻破還是不太容易的。所以我們先把一些條件放一放,先從最簡單的思路入手。
最簡情況
首先我們需要解決一個問題,就是當不存在?的時候,我們如何求出abc子序列的個數呢?比如acabac,我們怎麼求出其中abc子序列的數量呢?答案是2,這個2是怎麼來的呢?
因為要保證b出現在a的後面,c出現在b的後面,我們當然可以直接用三重迴圈去列舉所有的組合,但這顯然不是最好的方法。我們仔細想一下,這是一個有限制的組合問題,很容易發現每一個字母b都可以和它之前的所有a組成ab的配對,同樣每個字母c也可以和之前的每個序列ab組成abc的子序列。
那麼我們可以使用動態規劃的思路來求解,我們用三個變數d[0], d[1], d[2],分別維護a、ab和abc串的數量。當我們遇到a的時候,d[0]加1,遇到b的時候呢?d[1]加1嗎?顯然不對,因為b可以和之前的每一個a都組成ab,所以d[1]應該加的不是1而是d[0]。同樣,遇到c的時候,也一樣,d[2]應該加上d[1]。
這樣我們就可以求出在沒有?時滿足條件的子序列的數量了。
d = [0, 0, 0]
for i in range(n):
if s[i] == 'a':
d[0] += 1
elif s[1] == 'b':
d[1] += d[0]
else:
d[2] += d[1]
進階使用
現在我們已經解決了沒有?出現時的情況,那麼加上?的話應該怎麼辦呢?
我們進一步來分析,對於每一個?來說它其實都有三種選擇。比如a?c,展開之後會有aac,abc,acc這三種情況。那麼我們能不能在遇到?的時候把這三種情況全部都考慮進去呢?
當我們遇到?的時候,我們把d陣列拷貝成三份,分別用來儲存?=a, ?=b 和 ?=c的情況。我們先把陣列d拷貝成d1、d2和d3。我們用這三個陣列代表?取三種不同取值的情況,最後再把它們合併到一起,變成新的陣列d_new。
因為d1, d2和d3其實就是d,所以我們把它們最後整合在一起之後依然可以用d來表示。
到這裡都沒有問題對吧,其實這裡藏了一個trick。這個trick非常隱蔽,很難發現。就是我們在?=a的情況下,我們這裡d1[0] += 1其實是不對的,我們要加的不是1。我們來舉一個例子就明白了,比如a??c。
在這個字串當中我們存在兩個?,對於第一個?而言,它是a的時候d[0] += 1這沒有問題。但是當遇到第二個?的時候問題就來了,對於第一個問號等於a的情況而言,由於第二個問號有三種取值,那麼帶來的a的數量其實應該是3,而不是1。我們只加了1就是錯誤的。我們也可以反過來理解,對於第一個問號,我們整合了三種可能性。當我們第二個問號設定為a的時候,其實是背後是第一個問號帶來的3種可能,所以這裡要加的就不是1而是3。如果我們有3個問號呢?第三個問號顯然加的就是9了。也就是說對於第k個問號而言,。
這個trick注意到了,那麼AC也就是順理成章的事了。
n = int(input())
s = input()
ret = 0
Mod = int(1e9+7)
dp = [0, 0, 0]
cnt = 1
for i in range(n):
if s[i] == 'a':
dp[0] += cnt
elif s[i] == 'b':
dp[1] += dp[0]
elif s[i] == 'c':
dp[2] += dp[1]
else:
dp[2] = (3 * dp[2] + dp[1]) % Mod
dp[1] = (3 * dp[1] + dp[0]) % Mod
dp[0] = (3 * dp[0] + cnt) % Mod
cnt = (cnt * 3) % Mod
print(dp[2] % Mod)
今天選擇的是div3比賽當中通過人數最少,也就是最難的一題。但我們做下來會發現其實也就用到了最基礎的動態規劃的思想,雖然不容易想到解,但是難度其實還可以。
所以如果你想要提升自己的演算法能力,但是又擔心題目太難自己無法勝任的話,可以考慮做一做div3的一些比賽。相信我不會太難的。
今天的文章就到這裡,衷心祝願大家每天都有所收穫。如果還喜歡今天的內容的話,請來一個三連支援吧~(點贊、關注、轉發)