Gym 100431E Word Cover 題解:KMP上跑dp

題意:

給你一個串,問你他的每個前綴的最小重複單元,其中單元是可以重疊的,最後按順序輸出即可。比如樣例中abaabaa的最小重複單元爲abaa,所以相應輸出爲4。

樣例:

input : abaabaababa

output:1 2 3 4 5 3 4 5 3 10 3



kmp過程就不用多說了,現在我們利用next數組的性質來對問題進行求解。


我們首先用一個ans[maxn]數組來記錄最後的答案,且我們的字符串下標從0開始,顯然,我們ans[i]的最大值爲i+1,我們因此也用此值對其進行初始化。

現在我們考慮什麼情況下ans[i]可以得到更小的,更小他能達到多小。


其實這個顯然可以知道第i位的ans值只能從ans[next[i]]那裏獲得,這個就可以根據next數組的性質想一想就明白了,若存在更短的,到i的後面的這段就不成立了。


然後我們考慮i和next[i]位置的兩種情況:


第一種情況:i - next[i] ≤ next[i]





此時顯然就可以利用ans[next[i]]進行更新了,如果可以形成前面這段,那麼一定可以形成後面那段。


第二種情況:i - next[i] > next[i]




這種情況就是此題的難點所在了,乍看之下似乎這種情況下只能放棄用ans[next[i]]來更新ans[i]了,其實不然!!!

樣例就給我們了很好的反例,因爲樣例最後一位的答案是3!


我們用dp[k]來記錄最後ans是k的最大的下標,我們假設cnt = ans[next[i]],即在next[i]處的答案,然後如圖:




一個比較顯而易見的是cnt ≤ next[i]是肯定成立的,而此種假設下我們假設i - next[i] ≤ next[i],現在決定最終成敗的就只剩下dp[cnt]的具體位置了!!!


我們證明 i - dp[cnt] ≤ cnt 時的情況必然可以用cnt來更新ans[i]。

此時狀況完全如上圖所示,此時在dp[cnt]前已經得知必可由長度爲cnt的串來產生,而這cnt長得串同時也肯定是從0到next[i]中長度爲cnt的後綴。那麼根據next數組的性質,這段串與i-cnt ~ i這段是相同的。我們又假設了 i - cnt ≤ dp[cnt] ,因此我們的i-cnt ~ i這段必然可由與形成dp[cnt]長度相同的串來產生,同時這也是其所有可能的最小答案。


最後當next[i] = -1 的時候就沒什麼說的了,顯然上面說的這些都沒用了,直接就賦值給 ans[i] = i + 1,再更新一下 dp[ans[i]] = i 就可以了。


在此表達對此神作法的膜拜之情!

本弱渣的代碼如下:



#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>

using namespace std;

char word[250010];
int next[250010], ans[250010], dp[250010];

int main() {
	freopen("cover.in", "r", stdin);
	freopen("cover.out", "w", stdout);
	scanf("%s", word);
	int len = strlen(word), k = -1;
	next[0] = -1;
	for (int i = 1; i < len; i++) { 				// KMP構造next數組過程
		while (k != -1 && word[i] != word[k + 1]) k = next[k];
		if (word[i] == word[k + 1]) k++;
		next[i] = k;
	}
	for (int i = 0; i < len; i++) {
		ans[i] = i + 1;					//首先賦值最大的可能值i+1
		if (next[i] != -1) {
			int cnt = ans[next[i]];
			if (i - next[i] <= next[i] || i - dp[cnt] <= cnt) {
				ans[i] = cnt;
			}
		}
		dp[ans[i]] = i;					//用ans[i]去更新dp[ans[i]]
	}
	for (int i = 0; i < len - 1; i++) printf("%d ", ans[i]); printf("%d\n", ans[len - 1]);
	return 0;
}



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