zoukankan      html  css  js  c++  java
  • 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;
    }
    

     

  • 相关阅读:
    vue2.0是不支持通过下标来更改数组的,无法做到响应式
    C# 深拷贝 Bitmap对象示例
    vscode终端中文乱码
    TkbmMemTable使用总结
    openssl 证书概念介绍
    openssl源码介绍
    python变量赋值特性
    openssl安装
    github开源协议选择
    NLP 多分类神经网络
  • 原文地址:https://www.cnblogs.com/clrs97/p/8356530.html
Copyright © 2011-2022 走看看