题意:将一个数列分成连续的三段,每段必须有数字,问这三段反转后的数列的最小字典序的方案,并输出,注意:第一个数比后面所有都大
思路:因为第一个数最大,那么将整个数列反转后的字典序最小的后缀为第一段分开位置,但是要判断情况,如最后还要至少剩下两个数完成后两段,接下来找第二段的分开位置,不可以像刚刚那么找了,想这个例子,将第一段去掉后是这样的,1 3 2 1 100 如果和第一次一样的方法结果是1 100 1 2 3 ,但是应该是1 2 3 1 100,前四个为第二段,那么给如何处理,机智的人总是有办法,不是我(/ □ \),将这段反转,再加上一段这样的反转,还是上面的例子,变成这样100 1 2 3 1 100 1 2 3 1,然后求后缀数组,确定第二个分开位置,如果从第二个1分开,则它到倒数第二个位置的串就为原序列要求的结果,显然它不是最优,最优的是第一个1开始,为1
2 3 1 100 ,这就是为什么加上两次的原因
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int maxn=200010; int n,k; int rank1[maxn*2],tmp[maxn*2],sa[maxn*2],rev[maxn*2]; int str[maxn],ans[maxn]; bool compare_sa(int i,int j){ if(rank1[i]!=rank1[j]) return rank1[i]<rank1[j]; else{ int ri=i+k<=n? rank1[i+k]:-1; int rj=j+k<=n? rank1[j+k]:-1; return ri<rj; } } void construct_sa(int *str1){ for(int i=0;i<=n;i++){ sa[i]=i; rank1[i]=i<n? str1[i]:-1; } for(k=1;k<=n;k*=2){ sort(sa,sa+n+1,compare_sa); tmp[sa[0]]=0; for(int i=1;i<=n;i++){ tmp[sa[i]]=tmp[sa[i-1]]+(compare_sa(sa[i-1],sa[i])?1:0); } for(int i=0;i<=n;i++) rank1[i]=tmp[i]; } } int main(){ int nn; scanf("%d",&nn); for(int i=0;i<nn;i++) scanf("%d",&str[i]); for(int i=0;i<nn;i++) rev[nn-1-i]=str[i]; n=nn;construct_sa(rev); int pos1,pos2; for(int i=0;i<nn;i++){ pos1=nn-sa[i]; if(pos1>=1&&nn-pos1>=2) break; } for(int i=pos1-1;i>=0;i--) printf("%d\n",str[i]); int kk=0; for(int i=nn-1;i>=pos1;i--) rev[kk++]=str[i]; for(int i=nn-1;i>=pos1;i--) rev[kk++]=str[i]; n=kk; construct_sa(rev); for(int i=0;i<=n;i++){ pos2=pos1+n/2-sa[i]; if(pos2-pos1>=1&&nn-pos2>=1) break; } for(int i=pos2-1;i>=pos1;i--) printf("%d\n",str[i]); for(int i=nn-1;i>=pos2;i--) printf("%d\n",str[i]); return 0; }
时间: 2024-10-03 06:23:23