動態規劃解不包含相同數字的子串個數問題

2021-06-27 16:24:48 字數 3251 閱讀 5515

比賽描述

仙靈女巫露露,對於魔法的熱忱可是超出常人,要是發現了什麼上古遺留下的魔法,她總是想方設法地獲得,然後研究分析。而最近,他又從**小法師維嘉那裡獲得了乙個「奇怪」的魔法卷軸;

這個魔法卷軸上有一大串數字,而且根據卷軸上的描述,這個魔法的威力指數來自於這一串數字中「魔法區間」的數量;

所謂「魔法區間」指的是一段連續的閉區間,且這段區間上的所有數字均不相同;

現在,露露想知道這個魔法的威力指數,你能幫幫她麼?

輸入先輸入乙個正整數t,表示樣例個數,1≤t≤10。

對於每乙個樣例,先輸入乙個正整數n,表示卷軸上的數字個數(1≤n≤

106);

再輸入n個整數,第i個數ai

,表示卷軸上第i個數(0≤ai

≤106)。

輸出對於每個樣例,輸出乙個正整數,即威力指數。

題目保證結果在int範圍內。

樣例輸入13

1 2 3

樣例輸出6

提示讀入資料請使用 scanf();

對於樣例,共有,,,,,,6個魔法區間,所以威力為6。

這道題資料範圍是1000000,理論上允許的最大時間複雜度為nlog(n)以下,首先考慮用動態規劃求解。

動態規劃的目標即為找到狀態轉移方程,假設一共n個數字在陣列num[n]中,其中包含前i個數字的解 為answer[i], 那麼我們就是要找到狀態轉移方程t,使

answer[i+1]=t(answer[i])             (1)

那麼answer[i+1]和answer[i+1]存在怎樣的關係呢?

明顯的,前i+1個數字所創造的「魔法空間」肯定是包含了前i個數字創造的「魔法空間」的,並且多出了一部分包含第i個數字的魔法空間。也就是說,(1)式可

進一步寫成:

answer[i+1]=answer[i]+f(i+1)       (2)

其中f(i+1)為包含第i+1個數字的魔法空間數量。

所以這道題的目標進一步轉化成:當我們有i+1個數字時,包含第i+1個數字的魔法空間有多少?

當然, 我們可以從i+1個數開始回溯,乙個乙個把數字新增到空集中,直到集合無法保持沒有重複數字,那麼這個集合的長度就是包含第i+1個數字的序列數(所有序列都依次為字尾子串行)。

以長度為4的序列 1 2 3 1 為例

當包含3個數字時, 含有的魔法空間數為6.即 1, 2, 3, 12,23,123 。

當加入第四個數字後,又增加了231,31,1 (不同於第乙個1), 所以1 2 3 1 的魔法空間數為9 。

求f的過程,可以盡量採取一些聰明的做法。 不斷地新增數字並每一次檢查序列是否合法是不明智的。

當我們從第i個數字向前回溯時,顯然到乙個位置j,且num[j]==num[i]時必須停止,因為集合已經重複了。 那麼我們從i+1回溯時,也不可能超過j這個位置。(因為依然包含了num[i]這個數,它與num[j]是重複的)。 我們設第i個數的前乙個相同值數字所在位置為mark[i].所以在每一次回溯時,我們可以設定乙個值limit(初始為0),表示我們可以達到的最前方的位置。然後在回溯的過程中,對於每乙個k(klimit) . 因為我們不可能超過mark[k]這個位置。

那麼回溯將在以下條件結束:

達到limit 或者num[k]==num[i+1].

回溯的距離也就是f(i+1)。 從而我們得到了完整的狀態轉移方程。

由以上思路完成的**如下:

#include#includeusing namespace std;

int f(int n)

int num[1000000];

int mark[1000000];

int main()

int sum=0;

for(i=0;i=limit;j--)

mark[i]=j+1;

// cout<

不幸的是,以上演算法在南郵acm系統上發生了tle (time limit exceed)。

回顧我們的做法和我們的初衷, 會發現如果我們面臨的是n個完全不同的數字,那麼每次在第i個位置回溯的距離都將是i, 演算法的時間複雜度為可怕的on2 .

看看我們**犯傻了呢?

當我們從第i個數字向前回溯時,顯然到乙個位置j,且num[j]==num[i]時必須停止,因為集合已經重複了。 那麼我們從i+1回溯時,也不可能超過j這個位置。

這個做法並非smart,實際上,我們應該儲存的不應該是每乙個數字前乙個數字的位置,儘管這樣可以幫助我們減少回溯的距離,不過仔細想想,在第i+1個位置能回溯的距離,不是僅僅和第i個位置能回溯的距離有關嗎?

我們來整理一下思路,假設我們之前有i個數字,並且已經得到了乙個解(動態規劃的立足點),那麼當加入第i+1個數字時,得到的解可以通過求f(i+1)(包含第i+1個數字的區間數)來得到, 而 這個區間數其實就是包含第i+1個數字的最長的魔法空間的長度。 而這個長度,實際上就是包含第i個數字的最長的魔法空間的長度再加上1!

沒錯,對於f來講, 依然是乙個動態規劃問題,不過是乙個非常簡單的動態規劃問題:

f(i+1)=f(i)+1           (3)

但是我們仍然要拿到mark[num[i+1]], 因為前乙個與第i+1個數字相同的數字j可能包含於f(i)所代表的魔法區間中, 而現在因為加入了i+1個數字,所以j會「截斷」f(i).

所以對於這個問題,我們不必再去回溯(也就是說現在的時間複雜度是on ), 只需在考察第i+1個數字時,分析兩種情況:

包含第i個數字的最長空間不含有與i+1 相同的數字: 那麼f(i+1)=f(i)+1 

否則 f(i+1)= i+1 -j .

注意文章中的變數名稱與程式不是完全對應的。程式的實現手段可能更加抽象, 但是思路就是上文所述。

**敬上

#include#includeusing namespace std;

int num[1000001];

int limit[1000001];

int max_[1000001];

int main()

{ int i,j,n,t;

scanf("%d",&t);

// scanf_(t);

while(t--)

{scanf("%d",&n);

// scanf_(n);

memset(max_,-1,sizeof(max_));

for(i=0;i

動態規劃解最大公共子串

子串是連續的 一 刻畫最優解結構特徵 用c i,j 表示以a i b j 結尾的最大公共子串中的字元數。則max c i,j 0 i len a 1,0 j len b 1 表示a和b最大公共子串的字元數。同時在求解過程中可以確定i和j,這樣也就確定了這個公共子串。二 遞迴定義最優解的值 c i,j...

DP動態規劃 最大數字子串

最大數字子串 time limit 1500 ms memory limit 10000 kb judge type multi cases total submit 1215 245 users accepted submit 397 209 users page view 6115 font s...

判斷數字中是否包含兩個相同的子串 華為oj

描述 判斷給定的乙個數字,把它當成字串以後,該字串中是否包含相同的兩個子串,子串的長度要求大於等於2。比如 12123,該數字包含兩個 12 子串 又比如 1223122,該數字包含兩個 122 子串。執行時間限制 無限制 記憶體限制 無限制 輸入 待判斷的正整型數字,最大長度為9。輸出 0 不包含...