hiho一下 第六十周

2021-07-05 12:08:30 字數 4907 閱讀 6857

給定只包含字母的兩個字串a,b,求a,b兩個字串的最長公共子串行,要求構成子串行的子串長度都必須大於等於3。

比如」abcdefghijklmn」和」ababceghjklmn」,其最長滿足題意要求的子串行為」abcjklmn」,其由公共子串」abc」和」jklmn」組成。

這裡我們要注意子串和子串行的區別:

比如」abcdefghijklmn」和」ababceghjklmn」的最長公共子串就只是」jklmn」了。

首先我們來複習一道經典的題目:

給定只包含字母的兩個字串a,b,求a,b兩個字串的最長公共子串行。

比如"abcde"和"abdfg"的最長公共子串行為"abd"

對於最長公共子串行,我們知道解法為

dp[0][0..j] = 0    // 邊界

dp[0..i][0] = 0 // 邊界

for i = 1 .. n

for j = 1 .. m

if a[i] == b[j] then

dp[i][j] = dp[i - 1][j - 1] + 1

else

dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

end if

end for

end for

而這一道題目是在最長公共子串行上加入了乙個條件:構成最長公共子串行的每乙個子串長度必須大於等於3.

乙個簡單的想法:我們求出最長公共子串行,然後將其中長度小於3的部分去掉。

顯然,這是不對的。

舉個例子:」aaabaa」和」acaaaca」的最長子串行為」aaaaa」。其對應關係為:

a aaba a

acaa aca

因為在」acaaaca」中第乙個字母a長度為1,所以我們需要去掉它,對應的我們也去掉了」aaabaa」中第乙個字母a。

.  aaba a

. caa aca

此時構成」aaabaa」和」acaaaca」公共子串行的3個子串為」aa」,」a」和」a」,長度都小於了3,所以全部刪去,則得到了新的公共子串行長度為0。

這顯然不正確,因為實際有符合題意要求的公共子串行:

aaa baa

ac aaa ca

其中包含有長度為3的公共子串行。

對最大公共子串行的結果進行再次處理這個方法不可行,那麼我們只能從計算公共子串行的演算法著手。

首先我想我們可以做乙個預處理,用f[i][j]表示以a的第i個字母作為結尾的字首和以b的第j個字母作為結尾的字首的公共字尾的長度。這樣看上去似乎很繞,不如舉個例子:

a=」abcd」和b=」acbc」。f[3][4]的就表示a[1..3]和b[1..4]的公共字尾的長度,其中a[1..3]=」abc」,b[1..4]=」acbc」,其公共字尾為」bc」,所以f[3][4]=2.

預處理的偽**為:

for i = 1 .. n

for j = 1 .. m

if a[i] == b[j] then

f[i][j] = f[i - 1][j - 1] + 1

else

f[i][j] = 0

endif

endfor

endfor

有了這個預處理的陣列,我們可以在原來最大公共子串行上做這樣乙個改進:

dp[0][0..j] = 0    // 邊界

dp[0..i][0] = 0 // 邊界

for i = 1 .. n

for j = 1 .. m

if f[i][j] >= 3 then // 改進

dp[i][j] = dp[i - f[i][j]][j - f[i][j]] + f[i][j]

else

dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

end if

end for

end for

這個改進的意義為:當我們出現乙個長度大於3的子串時,我們就直接將這個子串合併入我們的子串行。

加入這個改進後,我們通過了樣例的資料,這樣看上去似乎就應該沒什麼問題了。

然而事實並不是這樣,在這道題目中還隱藏著陷阱:

比如」abcdef」和」abcxcdef」

根據我們演算法,上面這個例子算出的結果為4,然而其實際的結果應該為6,即」abc」和」def」兩個公共子串構成的子串行。

那麼出錯的原因在哪?就在字串」cdef」上。

我們計算結果出4是因為將」cdef」看做了乙個整體,而將」abcdef」分割成了」ab」和」cdef」。

在dp的過程中f[6][7] = 4,我們使用了dp[6][7] = dp[2][3] + 4,而dp[2][3] = 0,所以dp[6][7] = 4。

ab  cdef

abcxcdef

而實際上的最有解是將f[6][7]看作3,dp[6][7] = dp[3][4] + 3,其中dp[3][4] = 3,得到了dp[6][7] = 6。

abc  def

abcxcdef

也就是說,如果我們將f[i][j]>3的子串進行分割,有可能得到更優的情況。因此我們需要進一步的改進:

dp[0][0..j] = 0    // 邊界

dp[0..i][0] = 0 // 邊界

for i = 1 .. n

for j = 1 .. m

dp[i][j] = 0

if f[i][j] >= 3 then // 改進

for k = 3 .. f[i][j] // 列舉分割長度

dp[i][j] = max(dp[i][j], dp[i - k][j - k] + k)

end for

end if

dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

end for

end for

但是這樣的改進使得整個演算法的時間複雜度變為了o(n^3),當n=2100時,有可能會超時。

讓我們考慮一下如何進一步改進這個演算法。以上演算法複雜度高的地方在於對於每乙個(i, j),我們為了計算dp[i][j]都需要列舉分割長度k:

for k = 3 .. f[i][j]    // 列舉分割長度

dp[i][j] = max(dp[i][j], dp[i - k][j - k] + k)

end for

這一步實際上我們計算了max, k=3..f[i][j]。我們不妨把它記作dp1[i][j],即:

dp1[i][j] = max = max
同時

dp1[i-1][j-1] = max

= max

我們可以發現,dp1[i][j]的展開式中除了dp[i-3][j-3]+3這一項,是與dp1[i-1][j-1]中的每一項一一對應的,並且剛好大1。所以實際上dp[i-1][j-1]計算時列舉過分割長度,我們並不需要再次計算:

dp1[i][j] = max
最後得到我們新的偽**如下,其中dp[i][j][0]對應上文分析中的dp[i][j], dp[i][j][1]對應dp1[i][j]:

dp[0][0..j][0..1] = 0    // 邊界

dp[0..i][0][0..1] = 0 // 邊界

for i = 1 .. n

for j = 1 .. m

dp[i][j][1] = 0

if f[i][j] >= 3 then // 改進

dp[i][j][1] = max(dp[i][j][1], dp[i - 3][j - 3][0] + 3) // 以長度3為分割

if (f[i][j] > 3) then

//按照dp[i-1][j-1][1]的分割方式分割,即直接將(i,j)接在(i-1,j-1)後面

dp[i][j][1] = max(dp[i][j][1], dp[i - 1][j - 1][1] + 1)

end if

end if

dp[i][j][0] = max(dp[i-1][j][0], dp[i][j-1][0], dp[i][j][1])

end for

end for

至此我這道題目也算是完整的解出了。

這個題目是在經典的動態規劃題目《最長公共子串行》上做了一點修改。雖然只增加了乙個條件,不過難度增大很多。能想出乙個複雜度是o(n^2)的正確演算法不是很容易,需要仔細分析清楚各種情況。一不小心就會掉進各種陷阱裡。

很多選手都能夠想到經典最長子序列的改進演算法而獲得80分。

剩下的測試點則對應了演算法分析中提到的陷阱,所以能否找出這種特殊的例子也是解決這道題的關鍵。

不過微軟的出題人似乎沒有想太為難大家,資料並不是很強。在實際的比賽中,o(n^3)的演算法也能拿到滿分,最終該題目的通過率為9%。

很多o(n^2)的程式不能通過」babad」和」babacabad」這組資料。

#include 

#include

#include

using

namespace

std;

char a[2110], b[2110];

int dp[2110][2110][2], f[2110][2110];

int main()

}printf("%d\n", dp[len_a][len_b][1]);

return

0;}

hiho一下 第六十二周

在瀏覽網頁的時候,快取技術能夠迅速地顯示頁面。這裡我們對瀏覽器的快取技術進行簡化 我們認為瀏覽器的快取大小為m,表示快取可以儲存m個頁面。當使用者訪問url時,瀏覽器會先到快取中查詢是否有該頁面的記錄,如果有則直接從快取中提取資料 否則,會傳送網路請求,從internet獲取該頁面,並將該頁面放入快...

第六十周學習生活總結

今天是2020年4月12日。天氣真的是超級熱,中午那一會兒覺得都能穿短袖了,可誰也想不到兩天前還下了雪 沒忽悠你們,是真的下雪了,雖然沒有積雪,但那天的雨夾雪是絕對能讓你裹緊自己的小棉襖的 做了這乙個月的後台,我發現後台的套路很簡單,而且頁面寫起來真的很方便啊,基本就是乙個模板一直加加加。不過後台也...

hiho一下 第十周 分治

時間限制 10000ms 單點時限 1000ms 記憶體限制 256mb 描述在參與過了美食節之後,小hi和小ho在別的地方又玩耍了一陣子,在這個過程中,小ho得到了乙個非常有意思的玩具 一棵由小球和木棍連線起來的二叉樹!小ho對這棵二叉樹愛不釋手,於是給它的每乙個節點都標記了乙個標號 乙個屬於a....