本文主要是介绍poj 3581,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
题意:将一个数列分成连续的三段,每段必须有数字,问这三段反转后的数列的最小字典序的方案,并输出,注意:第一个数比后面所有都大
思路:因为第一个数最大,那么将整个数列反转后的字典序最小的后缀为第一段分开位置,但是要判断情况,如最后还要至少剩下两个数完成后两段,接下来找第二段的分开位置,不可以像刚刚那么找了,想这个例子,将第一段去掉后是这样的,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 ,这就是为什么加上两次的原因
//poj 3581
const int maxn = 200010;
const int inf = 0x3f3f3f3f;
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 argc, const char * argv[]) {// insert code here...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]);//std::cout << "Hello, World!\n";return 0;
}
这篇关于poj 3581的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!