題解 NOI2014 動物園

2021-09-26 06:53:39 字數 1547 閱讀 2191

luogu

輸入n組資料,每組資料報含乙個字串s,對於每個s,求出它的num陣列。

num陣列的定義是:字串s的前i個字元構成的子串,既是它的字尾同時又是它的字首,並且該字尾與該字首不重疊,將這種字串的數量記作num[i]。

為了避免大量的輸出,你不需要輸出num[i]分別是多少,你只需要輸出所有(num[i]+1)的乘積,對1,000,000,007取模的結果即可。

這是一道很經典的kmp。還不會kmp的同學可以先看一下這篇部落格,這裡就不重複介紹了

那麼這道題與普通的kmp有什麼不同點呢?

我們發現最大的不同點有兩個,乙個是原來的next[i]表示的是字串前i位的最長相同前字尾,而num[i]卻要求出相同前字尾的總數。第二個是,這裡要求字尾和字首不重疊。

發現了不同點,那麼就只要在原來的kmp**上對於這兩個點分別改動一下就行了

大家都知道(next[i]>0)表示字串前i位有相同前字尾(如果next[i]=0就表示沒有),而它的值就是最長的乙個字首的結尾。

ok,那麼第乙個點就迎刃而解了

第2長的公共前字尾一定比最長的短吧

那它一定包含在s[0]-s[next[i]]裡面吧

那麼這裡面最長的是哪個?

next[next[i]]

以此類推,next[i],next[next[i]],next[next[next[next[i]]]…都是相同字首的結尾,只要它們不是0,就代表num[i]多乙個,那麼把num[i]++就行了

是不是感覺和manacher有點像?

這樣能解決第乙個點,但不能解決第二個,因為這樣求出來的前字尾可能會有重疊

現在解決第二個點就很簡單啦,找到第乙個小於一半的num,

while

(k*2

>j+1)

其它就和普通的kmp一樣

完整**:

#include

using

namespace std;

const

int maxn=

1e6+10;

const

long

long mod=

1e9+7;

int next[maxn]

,num[maxn]

;int

main()

if(s[j]

==s[k]

) k++

; next[j+1]

=k; num[j+1]

=num[k]+1

;}k=0;

long

long

int ans=1;

for(

int j=

1;j(s[j]

==s[k]

) k++

;while

(k*2

>j+1)

ans=ans*

(num[k]+1

)%mod;

} cout<}return0;

}

2019-8-17

NOI2014 動物園題解

2種解法,sb o nlogn 和 sb o n 聽到過另一種牛逼 o n 感覺有點假就沒寫。sb o n log n 首先都知道 nxt 的指向關係可以構成一顆內向樹,隨便二分一下就行了。sb o n log n includeusing namespace std const int n 100...

題解 P2375 NOI2014 動物園

luogu 先用裸的 kmp 求出 fail next 陣列 隨便叫的無所謂啦 和不管重疊時的數量記為 ans ans 的定義類似於 fail next 然後再跑一遍,求出 frac 的數量,可結合 理解 includeusing namespace std int n,l,fail 1000010...

洛谷 P2375 NOI2014 動物園

其實對於乙個sum i 其值就等於sum next i sum next next i 1,然後我們可以記憶化,然後題目裡又有乙個限制,就是前字尾不能重合,那我們就找到最大但是不重合的那種情況,然後往小了列舉就好了.最後答案說要取模,所以不要忘了 ljx xhy yyq 排名無先後 1 includ...