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(nlog 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
",x,y);
  }
}

  

C. Not common palindromes

将两个串插入同一棵Palindromic Tree然后统计次数即可。

时间复杂度$O(nlog 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
",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;
}

 

原文地址:https://www.cnblogs.com/clrs97/p/8356530.html