1. 程式人生 > >[洛谷P2375] [NOI2014]動物園

[洛谷P2375] [NOI2014]動物園

幫助 noi2014 math 保持 存在 題意 href 正整數 bit

洛谷題目鏈接:[NOI2014]動物園

題目描述

近日,園長發現動物園中好吃懶做的動物越來越多了。例如企鵝,只會賣萌向遊客要吃的。為了整治動物園的不良風氣,讓動物們憑自己的真才實學向遊客要吃的,園長決定開設算法班,讓動物們學習算法。

某天,園長給動物們講解KMP算法。

園長:“對於一個字符串 \(S\) ,它的長度為 \(L\) 。我們可以在 \(O(L)\) 的時間內,求出一個名為next的數組。有誰預習了next數組的含義嗎?”

熊貓:“對於字符串 \(S\) 的前 \(i\) 個字符構成的子串,既是它的後綴又是它的前綴的字符串中(它本身除外),最長的長度記作 \(next[i]\)。”

園長:“非常好!那你能舉個例子嗎?”

熊貓:“例 \(S\)abcababc,則 \(next[5]=2\) 。因為 \(S\) 的前 \(5\) 個字符為abcabab既是它的後綴又是它的前綴,並且找不到一個更長的字符串滿足這個性質。同理,還可得出 \(next[1] = next[2] = next[3] = 0\)\(next[4] = next[6] = 1\)\(next[7] = 2\)\(next[8] = 3\) 。”

園長表揚了認真預習的熊貓同學。隨後,他詳細講解了如何在 \(O(L)\) 的時間內求出next數組。

下課前,園長提出了一個問題:“KMP算法只能求出next數組。我現在希望求出一個更強大num

數組一一對於字符串 \(S\) 的前 \(i\) 個字符構成的子串,既是它的後綴同時又是它的前綴,並且該後綴與該前綴不重疊,將這種字符串的數量記作 \(num[i]\) 。例如 \(S\)aaaaa,則 \(num[4] = 2\) 。這是因為 \(S\) 的前 \(4\) 個字符為aaaa,其中aaa都滿足性質‘既是後綴又是前綴’,同時保證這個後綴與這個前綴不重疊。而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]動物園