[洛谷P2375] [NOI2014]動物園
洛谷題目鏈接:[NOI2014]動物園
題目描述
近日,園長發現動物園中好吃懶做的動物越來越多了。例如企鵝,只會賣萌向遊客要吃的。為了整治動物園的不良風氣,讓動物們憑自己的真才實學向遊客要吃的,園長決定開設算法班,讓動物們學習算法。
某天,園長給動物們講解KMP算法。
園長:“對於一個字符串 \(S\) ,它的長度為 \(L\) 。我們可以在 \(O(L)\) 的時間內,求出一個名為next
的數組。有誰預習了next
數組的含義嗎?”
熊貓:“對於字符串 \(S\) 的前 \(i\) 個字符構成的子串,既是它的後綴又是它的前綴的字符串中(它本身除外),最長的長度記作 \(next[i]\)。”
園長:“非常好!那你能舉個例子嗎?”
熊貓:“例 \(S\) 為abcababc
,則 \(next[5]=2\) 。因為 \(S\) 的前 \(5\) 個字符為abcab
,ab
既是它的後綴又是它的前綴,並且找不到一個更長的字符串滿足這個性質。同理,還可得出 \(next[1] = next[2] = next[3] = 0\) , \(next[4] = next[6] = 1\) , \(next[7] = 2\) , \(next[8] = 3\) 。”
園長表揚了認真預習的熊貓同學。隨後,他詳細講解了如何在 \(O(L)\) 的時間內求出next
數組。
下課前,園長提出了一個問題:“KMP算法只能求出next
數組。我現在希望求出一個更強大num
aaaaa
,則 \(num[4] = 2\) 。這是因為 \(S\) 的前 \(4\) 個字符為aaaa
,其中a
和aa
都滿足性質‘既是後綴又是前綴’,同時保證這個後綴與這個前綴不重疊。而aaa
雖然滿足性質‘既是後綴又是前綴’,但遺憾的是這個後綴與這個前綴重疊了,所以不能計算在內。同理, \(num[1] = 0,num[2] = num[3] = 1,num[5] = 2\) 。”
最後,園長給出了獎勵條件,第一個做對的同學獎勵巧克力一盒。聽了這句話,睡了一節課的企鵝立刻就醒過來了!但企鵝並不會做這道題,於是向參觀動物園的你尋求幫助。你能否幫助企鵝寫一個程序求出 \(num\)
特別地,為了避免大量的輸出,你不需要輸出 \(num[i]\) 分別是多少,你只需要輸出所有\(( num[i]+1 )\)的乘積,對 \(1,000,000,007\) 取模的結果即可。
輸入輸出格式
輸入格式:
第 \(1\) 行僅包含一個正整數 \(n\) ,表示測試數據的組數。
隨後 \(n\) 行,每行描述一組測試數據。每組測試數據僅含有一個字符串 \(S\) , \(S\) 的定義詳見題目描述。數據保證 \(S\) 中僅含小寫字母。輸入文件中不會包含多余的空行,行末不會存在多余的空格。
輸出格式:
包含 \(n\) 行,每行描述一組測試數據的答案,答案的順序應與輸入數據的順序保持一致。對於每組測試數據,僅需要輸出一個整數,表示這組測試數據的答案對 \(1,000,000,007\) 取模的結果。輸出文件中不應包含多余的空行。
輸入輸出樣例
輸入樣例#1:
3
aaaaa
ab
abcababc
輸出樣例#1:
36
1
32
說明
測試點編號 | 約定 |
---|---|
1 | \(N≤5,L≤50\) |
2 | \(N≤5,L≤200\) |
3 | \(N≤5,L≤200\) |
4 | \(N≤5,L≤10,000\) |
5 | \(N≤5,L≤10,000\) |
6 | \(N≤5,L≤100,000\) |
7 | \(N≤5,L≤200,000\) |
8 | \(N ≤ 5, L ≤ 500,000\) |
9 | \(N ≤ 5, L ≤ 1,000,000\) |
10 | \(N ≤ 5, L ≤ 1,000,000\) |
一句話題意: 給出一個字符串,要求出所有前綴中,該前綴的前綴和後綴相同的數量(前綴後綴不重疊),將該前綴的答案計為\(num[i]\),問\(\displaystyle \prod_{i=1}^{n} (num[i]+1)\).
題解: 因為這個\(num\)數組是可以由KMP的\(next\)數組來統計出的.但是如果每次都一個個統計過來的話,一次操作最多可以是\(O(n)\)的(也就是從第\(n\)位一直到第1位統計過來).顯然如果這樣倒著推的話復雜度是接受不了的.
所以在這裏我們考慮維護一個\(num\)數組的前綴和,但是這裏的\(num\)數組的含義要改一下,我們用\(num[i]\)表示在第1到第\(i\)位中前綴和後綴相同的數量(前綴後綴重疊也計算進答案).然後我們對這個數組進行前綴和.與此同時我們的\(next\)數組的含義也要改一下,\(next[i]\)表示1~\(i\)位中前綴和後綴相同的最大長度.
因為我們計算的\(num[i]\)與題目所給的\(num[i]\)並不同,所以算題目最終給出的\(num[i]\)的時候,就可以直接用我們算的\(num[\left\lceil{\frac{i}{2}}\right\rceil]\)計入總答案.
最後提一句:千萬不要對著題解的代碼改,邊界細節一定要自己想清楚,不然會被題解繞暈的!!
#include<bits/stdc++.h>
using namespace std;
const int yyj = 1e9+7;
const int MAXLEN = 1e6+5;
typedef int _int;
#define int long long
int T, n, nex[MAXLEN], num[MAXLEN], ans = 0;
char s[MAXLEN];
_int main(){
ios::sync_with_stdio(false);
cin >> T;
while(T--){
memset(nex, 0, sizeof(nex));
memset(num, 0, sizeof(num)); ans = 1, num[1] = 1;
cin >> s+1; n = strlen(s+1), nex[2] = 1;
for(int i=2, j=0;i<=n;i++){
while(j && s[j+1] != s[i]) j = nex[j];
j += s[i] == s[j+1], nex[i] = j, num[i] = num[j]+1;
}
for(int i=1, j=0;i<=n;i++){
while(j && s[j+1] != s[i]) j = nex[j];
j += s[j+1] == s[i];
while(j*2 > i) j = nex[j];
ans = (ans*(num[j]+1)) % yyj;
}
cout << ans << endl;
}
return 0;
}
[洛谷P2375] [NOI2014]動物園