URAL Palindromic Contest
A. Non-palidromic cutting
考虑无解的情形:只能是形如$aaaaa$、$aaabaaa$、$abababa$这三种情况。
有解时,对于最小划分,答案必定是$1$或者$2$,判断整个串是否是回文串即可。
对于最大划分,设$f[i]$表示前$i$个字符的最大划分,则$f[i]=\max(f[j]+1)$,其中$[j+1,i]$有解,这里将限制放宽是因为放宽后最优解不会变化,且条件更加好判断。
可行的$j$可以根据$aaaaa$、$abababa$分奇偶得出两个上界,对于$aaabaaa$,最多只有一个$j$不可行,因此DP的同时分奇偶维护出前缀最大值和次大值即可$O(1)$转移。
时间复杂度$O(n)$。
#include<cstdio> #include<cstring> const int N=200010; int n,i,j,k,x,y,f[N],g[N],dp[N],pre[N][2][2];char a[N]; inline void umin(int&a,int b){a>b?(a=b):0;} inline void umax(int&a,int b){a<b?(a=b):0;} inline int max(int a,int b){return a>b?a:b;} bool checkNO(){ //aaaaa if(f[n]<=1)return 1; //aaaabaaaa if(n&1){ //aaaabaaaa int m=(n+1)/2; if(f[n]==m+1&&f[m-1]<=1&&a[1]==a[n])return 1; //ababababababa if(g[n]<=1&&g[n-1]<=2)return 1; } return 0; } int calmin(){ for(int i=1;i<=n;i++)if(a[i]!=a[n-i+1])return 1; return 2; } int main(){ scanf("%s",a+1); n=strlen(a+1); for(i=1;i<=n;i++){ f[i]=g[i]=i; if(i>1&&a[i]==a[i-1])f[i]=f[i-1]; if(i>2&&a[i]==a[i-2])g[i]=g[i-2]; } if(checkNO())return puts("-1"),0; for(x=0;x<2;x++)for(y=0;y<2;y++)pre[0][x][y]=-N; pre[0][0][0]=0; for(i=1;i<=n;i++){ dp[i]=-N; j=f[i]-2; if(j>=0)dp[i]=pre[j][i&1][0]+1; umin(j,max(g[i],g[i-1]-1)-1); k=-N; if(f[i]>2)if(i-f[i]<=f[i]-2-f[f[i]-2]&&a[i]==a[f[i]-2])k=dp[i-((i-f[i]+1)*2+1)]; if(j>=0){ if(pre[j][i&1^1][0]==k)umax(dp[i],pre[j][i&1^1][1]+1); else umax(dp[i],pre[j][i&1^1][0]+1); } for(x=0;x<2;x++)for(y=0;y<2;y++)pre[i][x][y]=pre[i-1][x][y]; if(dp[i]>pre[i][i&1][0])pre[i][i&1][1]=pre[i][i&1][0],pre[i][i&1][0]=dp[i]; else umax(pre[i][i&1][1],dp[i]); } printf("%d %d",calmin(),dp[n]); }
B. 100500 palidnromes
考虑DP,对于每个位置为结尾的回文串的长度可以划分为$O(\log n)$个等差数列,分别转移即可。
时间复杂度$O(n\log n)$。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int N=300010; char s[N];int d[N][2],f[N][2]; struct P{ int d[3]; P(){} P(int a,int b,int c){d[0]=a;d[1]=b;d[2]=c;} int&operator[](int x){return d[x];} }a[32],b[32],c[32]; inline void up(int f[][2],int x,int y){ if(y<=0)return; int p=y&1; if(f[x][p]<0)f[x][p]=y;else f[x][p]=min(f[x][p],y); } inline void make(int f[][2],int x,int y){if(y>0)f[x][y&1]=y;} void MinPalindromeSpilt(char*s){ int n=strlen(s); memset(a,0,sizeof a); memset(b,0,sizeof b); memset(c,0,sizeof c); memset(d,0,sizeof d); memset(f,0,sizeof f); for(int i=0;i<=n;i++)d[i][0]=1000000000,d[i][1]=1000000001; for(int ca=0,j=0;j<n;j++){ int cb=0,cc=0,r=-j-2; for(int u=0;u<ca;u++){ int i=a[u][0]; if(i>=1&&s[i-1]==s[j])a[u][0]--,b[cb++]=a[u]; } for(int u=0;u<cb;u++){ int i=b[u][0],d=b[u][1],k=b[u][2]; if(i-r!=d){ c[cc++]=P(i,i-r,1); if(k>1)c[cc++]=P(i+d,d,k-1); }else c[cc++]=P(i,d,k); r=i+(k-1)*d; } if(j>=1&&s[j-1]==s[j])c[cc++]=P(j-1,j-1-r,1),r=j-1; c[cc++]=P(j,j-r,1),ca=0; P&h=c[0]; for(int u=1;u<cc;u++){ P&x=c[u]; if(x[1]==h[1])h[2]+=x[2];else a[ca++]=h,h=x; } a[ca++]=h; if((j+1)%2==0)f[j+1][0]=j+1,f[j+1][1]=1000000001; else f[j+1][0]=1000000000,f[j+1][1]=j+1; for(int u=0;u<ca;u++){ int i=a[u][0],e=a[u][1],k=a[u][2]; r=i+(k-1)*e; up(f,j+1,f[r][0]+1),up(f,j+1,f[r][1]+1); if(k>1)up(f,j+1,d[i+1-e][0]),up(f,j+1,d[i+1-e][1]); if(i+1-e>=0){ if(k>1)up(d,i+1-e,f[r][0]+1),up(d,i+1-e,f[r][1]+1); else make(d,i+1-e,f[r][0]+1),make(d,i+1-e,f[r][1]+1); } } } } int main(){ scanf("%s",s); MinPalindromeSpilt(s); int n=strlen(s); for(int i=1;i<=n;i++){ int x=f[i][1],y=f[i][0]; if(x<1||x>N)x=-1; if(y<1||y>N)y=-2; printf("%d %d\n",x,y); } }
C. Not common palindromes
将两个串插入同一棵Palindromic Tree然后统计次数即可。
时间复杂度$O(n\log n)$。
#include<cstdio> const int N=400010,S=26; int Case,cas,i,j,all,son[N][S],fail[N],cnt[N][2],len[N],text[N],last,tot,ans[3];char s[N]; inline int newnode(int l){ for(int i=0;i<S;i++)son[tot][i]=0; cnt[tot][0]=cnt[tot][1]=0,len[tot]=l; return tot++; } inline int getfail(int x){ while(text[all-len[x]-1]!=text[all])x=fail[x]; return x; } inline void add(int w,int p){ text[++all]=w; int x=getfail(last); if(!son[x][w]){ int y=newnode(len[x]+2); fail[y]=son[getfail(fail[x])][w]; son[x][w]=y; } cnt[last=son[x][w]][p]++; } int main(){ scanf("%d",&Case); for(cas=1;cas<=Case;cas++){ newnode(0),newnode(-1); text[0]=-1,fail[0]=1; scanf("%s",s); for(i=0;s[i]>='a';i++)add(s[i]-'a',0); scanf("%s",s); for(i=all=last=0;s[i]>='a';i++)add(s[i]-'a',1); for(i=tot-1;~i;i--){ cnt[fail[i]][0]+=cnt[i][0]; cnt[fail[i]][1]+=cnt[i][1]; if(len[i]>0){ if(cnt[i][0]>cnt[i][1])ans[0]++; if(cnt[i][0]==cnt[i][1]&&cnt[i][0])ans[1]++; if(cnt[i][0]<cnt[i][1])ans[2]++; } } printf("Case #%d: %d %d %d\n",cas,ans[0],ans[1],ans[2]); for(last=tot=all=i=0;i<3;i++)ans[i]=0; } }
D. Subpalindrome pairs
正反两遍Manacher求出以每个位置为结尾/开头的回文子串个数,然后相乘即可。
时间复杂度$O(n)$。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=600010; int n,m,i,r,p,f[N];char a[N],s[N];long long v[N],g[N],ans; void work(){ for(i=1;i<=n;i++)s[i<<1]=a[i],s[i<<1|1]='#'; s[0]='$',s[1]='#',s[m=(n+1)<<1]='@'; for(r=p=0,f[1]=1,i=2;i<m;i++){ for(f[i]=r>i?min(r-i,f[p*2-i]):1;s[i-f[i]]==s[i+f[i]];f[i]++); if(i+f[i]>r)r=i+f[i],p=i; } for(i=0;i<=n+5;i++)v[i]=0; for(i=2;i<=n*2;i++)v[(i+1)/2]++,v[(i+f[i])/2]--; for(i=1;i<=n+5;i++)v[i]+=v[i-1]; } int main(){ scanf("%s",a+1); n=strlen(a+1); work(); for(i=0;i<=n+5;i++)g[i]=v[i]; reverse(g+1,g+n+1); reverse(a+1,a+n+1); work(); for(i=2;i<=n;i++)ans+=v[i-1]*g[i]; printf("%lld",ans); }
E. OEIS A216264
限制条件非常强,考虑爆搜打表,用可撤销的回文树快速判断即可。
#include<iostream> #include<string> using namespace std; int n,i;string f[70]; int main(){ f[1]="2"; f[2]="4"; f[3]="8"; f[4]="16"; f[5]="32"; f[6]="64"; f[7]="128"; f[8]="252"; f[9]="488"; f[10]="932"; f[11]="1756"; f[12]="3246"; f[13]="5916"; f[14]="10618"; f[15]="18800"; f[16]="32846"; f[17]="56704"; f[18]="96702"; f[19]="163184"; f[20]="272460"; f[21]="450586"; f[22]="738274"; f[23]="1199376"; f[24]="1932338"; f[25]="3089518"; f[26]="4903164"; f[27]="7728120"; f[28]="12099440"; f[29]="18825066"; f[30]="29112876"; f[31]="44767202"; f[32]="68461866"; f[33]="104153666"; f[34]="157657852"; f[35]="237510110"; f[36]="356158688"; f[37]="531729840"; f[38]="790476048"; f[39]="1170354912"; f[40]="1725978316"; f[41]="2535782098"; f[42]="3711932174"; f[43]="5414527812"; f[44]="7871216066"; f[45]="11405072346"; f[46]="16472995026"; f[47]="23719943936"; f[48]="34053444354"; f[49]="48748102876"; f[50]="69588917894"; f[51]="99071049592"; f[52]="140673083164"; f[53]="199235958260"; f[54]="281479919278"; f[55]="396717767314"; f[56]="557825677390"; f[57]="782576306282"; f[58]="1095448703190"; f[59]="1530103385844"; f[60]="2132734033216"; f[61]="2966632985826"; cin>>n; for(i=1;i<=n;i++)cout<<f[i]<<endl; }