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 ,这就是为什么加上两次的原因

#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

POJ 3581 后缀数组的相关文章

POJ 2774 后缀数组:求最长公共子串

思路:其实很简单,就是两个字符串连接起来,中间用个特殊字符隔开,然后用后缀数组求最长公共前缀,然后不同在两个串中,并且最长的就是最长公共子串了. 注意的是:用第一个字符串来判断是不是在同一个字符中,刚开始用了第二个字符的长度来判断WA了2发才发现. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<

poj 3261 后缀数组 找重复出现k次的子串(子串可以重叠)

题目:http://poj.org/problem?id=3261 仍然是后缀数组的典型应用----后缀数组+lcp+二分 做的蛮顺的,1A 但是大部分时间是在调试代码,因为模板的全局变量用混了,而自己又忘了,,,等西安邀请赛还有四省赛结束之后,该冷静反思下尝试拜托模板了 错误   :1.k用错,题目的k和模板的k用混; 2.还是二分的C()函数,这个其实跟前一篇<poj 1226 hdu 1238 Substrings 求若干字符串正串及反串的最长公共子串 2002亚洲赛天津预选题>的C函数

poj 3415 后缀数组分组+排序+并查集

Source Code Problem: 3415   User: wangyucheng Memory: 16492K   Time: 704MS Language: C++   Result: Accepted Source Code #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define N 510000

POJ 1226后缀数组:求出现或反转后出现在每个字符串中的最长子串

思路:这题是论文里的最后一道练习题了,不过最后一题竟然挺水的. 因为求的是未反转或者反转后,最长公共子串. 刚开始还真不知道怎么构建连接成一个字符串,因为需要有反转嘛! 但是其实挺简单的,把未反转的和反转后的字符串都连起来,中间用未出现过的字符隔开就行了!然后未反转的和反转的在同一组. 二分枚举最长的公共前缀长度,然后统计看看这个最长的长度在不在所有的组里,如果在就符合-- #include<iostream> #include<cstdio> #include<cstrin

POJ 3294 后缀数组:求不小于k个字符串中的最长子串

思路:先把所有的串连接成一个串,串写串之前用没出现过的字符隔开,然后求后缀:对height数组分组二分求得最长的公共前缀,公共前缀所在的串一定要是不同的,不然就不是所有串的公共前缀了,然后记下下标和长度即可. 刚开始理解错题意,然后不知道怎么写,然后看别人题解也不知道怎么意思,后面看了好久才知道题目意思理解错了. 时间四千多ms,别人才一百多ms,不知道别人怎么做的-- #include<iostream> #include<cstdio> #include<cstring&

POJ 1743 后缀数组:求最长不重叠子串

数据:这题弄了好久,WA了数十发,现在还有个例子没过,可却A了,POJ 的数组也太弱了. 10 1 1 1 1 1 1 1 1 1 1 这组数据如果没有那个n-1<10判断的话,输入的竟然是5,我靠-- 思路:这个题目关键的地方有两个:第一,重复的子串一定可以看作是某两个后缀的公共前缀,第二,把题目转化成去判定对于任意的一个长度k,是否存在长度至少为k的不重叠的重复的子串. 转化成判定问题之后,就可以二分去解答了.在验证判定是否正确时,我们可以把相邻的所有不小于k的height[]看成一组,然后

POJ 1226 后缀数组

题目链接:http://poj.org/problem?id=1226 题意:给定n个字符串[只含大小写字母],求一个字符串要求在n个串或者他们翻转后的串的出现过.输出满足要求的字符串的长度 思路:根据<<后缀数组——处理字符串的有力工具>>的思路,这题不同的地方在于要判断是否在反转后的字符串中出现 .其实这并没有加大题目的难度 . 只需要先将每个字符串都反过来写一遍, 中间用一个互不相同的且没有出现在字符串中的字符隔开,再将 n个字符串全部连起来, 中间也是用一 个互不相同的且没

poj 3261 后缀数组 找反复出现k次的子串(子串能够重叠)

题目:http://poj.org/problem?id=3261 仍然是后缀数组的典型应用----后缀数组+lcp+二分 做的蛮顺的,1A 可是大部分时间是在调试代码.由于模板的全局变量用混了,而自己又忘了.,,等西安邀请赛还有四省赛结束之后,该冷静反思下尝试拜托模板了 错误   :1.k用错,题目的k和模板的k用混; 2.还是二分的C()函数,这个事实上跟前一篇<poj 1226 hdu 1238 Substrings 求若干字符串正串及反串的最长公共子串 2002亚洲赛天津预选题>的C函

[poj 2274]后缀数组+最长公共子序列

题目链接:http://poj.org/problem?id=2774 后缀数组真的太强大了,原本dp是n^2的复杂度,在这里只需要O(n+m). 做法:将两个串中间夹一个未出现过的字符接起来,然后做一次后缀数组,得到的height相邻两个排名的后缀,在串中的位置如果满足在分界符左右两侧,就更新最长公共前缀.最后得到的最大值就是最长公共子序列. #include<algorithm> #include<cstdio> #include<cstring> using na