BZOJ 3790 神奇項鍊(迴文自動機+線段樹優化DP)
阿新 • • 發佈:2019-01-06
我們預處理出來以i為結尾的最長迴文字尾(迴文自動機的構建過程中就可以求出)然後就是一個區間覆蓋,因為我懶得寫貪心,就寫了線段樹優化的DP。
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int N=101010; const int INF=1e9; int L[N],dp[N],q[N],mn[N*5]; char s[N]; struct PAM{ int len[N],fa[N],size[N],num[N],tot,last,trans[N][27]; void init(){ len[0]=0;fa[0]=1;len[1]=-1;fa[1]=0; tot=1;last=0; memset(trans[1],0,sizeof(trans[1])); memset(trans[0],0,sizeof(trans[0])); } int new_node(int x){ int now=++tot; memset(trans[tot],0,sizeof(trans[tot])); len[now]=x; return now; } void ins(int c,int n){ int u=last; while(s[n-len[u]-1]!=s[n])u=fa[u]; if(trans[u][c]==0){ int now=new_node(len[u]+2); int v=fa[u]; while(s[n-len[v]-1]!=s[n])v=fa[v]; fa[now]=trans[v][c]; trans[u][c]=now; num[now]=num[fa[now]]+1; } last=trans[u][c];size[last]++; L[n]=len[last]; } }pam; void build(int l,int r,int now){ mn[now]=INF; if(l==r)return; int mid=(l+r)>>1; build(l,mid,now<<1); build(mid+1,r,now<<1|1); } void change(int l,int r,int x,int now){ if(l==r){mn[now]=dp[l];return;} int mid=(l+r)>>1; if(x>mid)change(mid+1,r,x,now<<1|1); else change(l,mid,x,now<<1); mn[now]=min(mn[now<<1],mn[now<<1|1]); } int getmin(int l,int r,int L,int R,int now){ if(l==L&&r==R)return mn[now]; int mid=(l+r)>>1; if(L>mid)return getmin(mid+1,r,L,R,now<<1|1); else if(R<=mid)return getmin(l,mid,L,R,now<<1); else return min(getmin(l,mid,L,mid,now<<1),getmin(mid+1,r,mid+1,R,now<<1|1)); } int main(){ while(scanf("%s",s+1)!=EOF){ int len=strlen(s+1); pam.init(); for(int i=1;i<=len;i++)pam.ins(s[i]-'a'+1,i); build(1,len,1); for(int i=1;i<=len;i++){ if(i-L[i]==0)dp[i]=1; else dp[i]=getmin(1,len,i-L[i],i-1,1)+1; change(1,len,i,1); } printf("%d\n",dp[len]-1); } return 0; }