CF932G-Palindrome Partition【PAM】
正題
題目鏈接:https://www.luogu.com.cn/problem/CF932G
題目大意
給出一個長度為nnn的字符串,將其分為kkk段(kkk為任意偶數(shù)),記為ppp。要求滿足對于任意iii都有pi=pk?i+1p_i=p_{k-i+1}pi?=pk?i+1?。求方案數(shù)。
1≤n≤1061\leq n\leq 10^61≤n≤106
解題思路
考慮將字符串化為S1SnS2Sn?1S3Sn?2...S_1S_nS_2S_{n-1}S_3S_{n-2}...S1?Sn?S2?Sn?1?S3?Sn?2?...這樣的形式,可以發(fā)現(xiàn)對于原本相同的段在這里就被表示為了一個偶回文子串。
那么問題就變?yōu)榱藙澐秩舾蓚€偶回文子串。設(shè)fif_ifi?表示前iii個的方案的話有一種比較簡單的做法,建立PAMPAMPAM后求出每個前綴的所有偶回文后綴,然后暴力轉(zhuǎn)移。
但是這樣的是O(n2)O(n^2)O(n2)的,時間復(fù)雜度不符合要求,考慮優(yōu)化。對于一個回文串來說它的所有回文后綴就是它的borderborderborder。而broderbroderbroder有一個性質(zhì)就是所有broderbroderbroder的長度可以被劃分成logloglog個等差數(shù)列。
我們可以在PAMPAMPAM上維護這些等差數(shù)列,記錄topitop_itopi?表示節(jié)點iii所在的等差數(shù)列的頂部,然后每次使用toptoptop往上跳。加入新的xxx節(jié)點(或者覆蓋以前的已經(jīng)有的節(jié)點)的時候累計一下自己作為末尾時所在等差數(shù)列方案和就好了
時間復(fù)雜度O(nlog?n)O(n\log n)O(nlogn)
code
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e6+10,P=1e9+7; int n,cnt,pos[N],len[N],dis[N],fa[N],top[N],ch[N][26]; char t[N],s[N];int f[N],g[N]; int jump(int x,int p){while(s[p-len[x]-1]!=s[p])x=fa[x];return x; } int Insert(int x,int p){x=jump(x,p);int c=s[p]-'a';if(!ch[x][c]){++cnt;len[cnt]=len[x]+2; int y=jump(fa[x],p);fa[cnt]=ch[y][c];y=cnt;dis[y]=len[y]-len[fa[y]];if(dis[y]!=dis[fa[y]])top[y]=y;else top[y]=top[fa[y]];ch[x][c]=y;}return ch[x][c]; } int main() {scanf("%s",t+1);n=strlen(t+1);if(n&1)return puts("0")&0;for(int i=1;i<=n;i+=2)s[i]=t[i/2+1];for(int i=2;i<=n;i+=2)s[i]=t[n-i/2+1];len[1]=-1;fa[0]=top[1]=cnt=1;for(int i=1;i<=n;i++)pos[i]=Insert(pos[i-1],i);f[0]=1;for(int i=1;i<=n;i++){for(int x=pos[i];x;x=fa[top[x]]){g[x]=f[i-len[top[x]]];if(x!=top[x])(g[x]+=g[fa[x]])%=P;if(!(i&1))(f[i]+=g[x])%=P;}}printf("%d\n",f[n]);return 0; }總結(jié)
以上是生活随笔為你收集整理的CF932G-Palindrome Partition【PAM】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《叱咤九州》快速成长秘籍教你如何智斗天下
- 下一篇: 怎么在抖音上发超过15秒的长视频?