Luogu P2375 [NOI2014]動物園|KMP+倍增+玄學
阿新 • • 發佈:2020-12-18
題目大意:
對於字串\(S\)的前\(i\)個字元構成的子串,既是它的字尾又是它的字首的字串中(它本身除外),最長的長度記作\(next[i]\)。
現在求一個\(num\)陣列,定義為對於字串\(S\)的前\(i\)個字元構成的子串,既是它的字尾同時又是它的字首,並且該字尾與該字首不重疊,將這種字串的數量記作\(num[i]\)。
輸出\(num_i+1\)的乘積。
題目思路:
sol 1:
既然題目都講到kmp了,那麼當然使用KMP打暴力啦。
將條件轉化為\(len*2<=i\),\(len\)為“對於字串\(S\)的前\(i\)個字元構成的子串,既是它的字尾又是它的字首的字串”的長度。
期望得分:50
sol 2:
不難發現,對於節點\(x\)
考慮KMP的過程中,將當前位置與\(next_i\)連邊,會形成一棵樹。那麼我們可以在這棵樹上通過樹上倍增找到最深的合法的節點,則該節點的父親均合法。
複雜度\(T \times NlogN\),應該能過,但T了。
加快讀並
程式碼:
#include<bits/stdc++.h> #define mod 1000000007 using namespace std; int t,f[1000100],fa[22][1000100],cnt[1000100],mi[21];char st[1000100]; int main() { t=int(getchar()-'0');getchar(); mi[0]=1; for (int i=1;i<=20;i++) mi[i]=mi[i-1]*2; for (int tt=1;tt<=t;tt++) { int len=1; st[1]=getchar(); if (st[1]<'a'||st[1]>'z') {cout<<"1\n";continue;} f[1]=0;cnt[1]=0; for (int i=2,j=0;;i++) { len++; st[i]=getchar(); if (st[i]<'a'||st[i]>'z') {len--;break;} while (st[i]!=st[j+1]&&j) { j=f[j]; } if (st[i]==st[j+1]) f[i]=j+1,j++,cnt[i]=cnt[j]+1;else f[i]=0,cnt[i]=0; bool flag=true; if (f[i]) fa[0][i]=f[i];else fa[0][i]=0,flag=false; for (int k=1;mi[k]<=i;k++) { fa[k][i]=fa[k-1][fa[k-1][i]]; } } long long ans=1; for (int i=2,sm=0;i<=len;i++) { int s=1,k=i; if (i>mi[sm]) sm++; for (int j=sm;j>=0;j--) { if ((f[fa[j][k]]<<1)>i) k=fa[j][k]; } if ((f[k]<<1)>i) { if ((f[fa[0][k]]<<1)<=i) k=fa[0][k]; else continue; } s+=cnt[k]; ans*=s; ans%=mod; } printf("%lld\n",ans); } return 0; }
正解是用dfs,有興趣的可自行尋找資料。