HDU 5442 Favorite Donut (2015年长春赛区网络赛F题)

来源:互联网 发布:成捷迅概预算软件 编辑:程序博客网 时间:2024/06/04 18:42

1.题目描述:点击打开链接

2.解题思路:本题利用后缀数组解决。这是我第一次用后缀数组解题,没想到第二发就过了,非常激动人心啊~。本题实际上就是SA的基本应用,首先求出原始串的SA,那么SA的最后一个后缀一定是顺时针方向上的最优解。这点不难证明,因为字典序比较时候,一定是从前往后比较的,这样,即使看做环形的字符串,这个大小关系依然不变,因此可以快速得到顺时针时候的答案。至于逆时针,这就需要借助一下height数组了,如果height[k]==n-sa[k-1]即LCP总是和前一个串是相同的,那么k--。为什么要这么做呢?因为假设LCP和sa[k-1]不相等,那么最优解就是sa[k],否则,因为根据题意描述,答案应该是下标较小的那个,即为翻转后的串中下标较大的那个。不难得知,下标较大的串一定是排名靠前的,所以要k--。

这样,正方向和反方向都得到答案后,再比较2个方向时候的字典序大小,即可确定最终的答案。

3.代码:

#include<iostream>#include<algorithm>#include<cassert>#include<string>#include<sstream>#include<set>#include<bitset>#include<vector>#include<stack>#include<map>#include<queue>#include<deque>#include<cstdlib>#include<cstdio>#include<cstring>#include<cmath>#include<ctime>#include<cctype>#include<complex>#include<functional>#pragma comment(linker, "/STACK:1024000000,1024000000")using namespace std;#define me(s)  memset(s,0,sizeof(s))#define rep(i,n) for(int i=0;i<(n);i++)typedef long long ll;typedef unsigned int uint;typedef unsigned long long ull;typedef pair <int, int> P;const int N=20000+10;struct SuffixArray{    int s[N];    int sa[N];    int rank[N];    int height[N];    int t[N],t2[N],c[N];    int n;    void clear(){n=0;me(sa);}    void build_sa(int m)    {        int i,*x=t,*y=t2;        for(i=0;i<m;i++)c[i]=0;        for(i=0;i<n;i++)c[x[i]=s[i]]++;        for(i=1;i<m;i++)c[i]+=c[i-1];        for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i;        for(int k=1;k<=n;k<<=1)        {            int p=0;            for(i=n-k;i<n;i++)y[p++]=i;            for(i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;            for(i=0;i<m;i++)c[i]=0;            for(i=0;i<n;i++)c[x[y[i]]]++;            for(i=0;i<m;i++)c[i]+=c[i-1];            for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];            swap(x,y);            p=1;x[sa[0]]=0;            for(i=1;i<n;i++)                x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;            if(p>=n)break;            m=p;        }    }    void build_height()    {        int i,k=0;        for(i=0;i<n;i++)rank[sa[i]]=i;        for(i=0;i<n;i++)        {            if(k)k--;            int j=sa[rank[i]-1];            while(s[i+k]==s[j+k])k++;            height[rank[i]]=k;        }    }};SuffixArray sa;char word[N],rev[N];int p0,p1;int n;int cmp(char*s,char*t,int p,int q)//比较正串和反串的字典序大小{    for(int i=0;i<n;i++)        if(s[(p+i)%n]!=t[(q+i)%n])        return s[(p+i)%n]>t[(q+i)%n];    return 2; //相等}void solve(int dir){    if(!dir)    {        sa.clear();        for(int i=0;i<n;i++)            sa.s[sa.n++]=word[i]-'a'+1;        sa.s[sa.n++]=0;        sa.build_sa(30);        p0=sa.sa[sa.n-1];    }    else    {        sa.clear();        for(int i=0;i<n;i++)            sa.s[sa.n++]=rev[i]-'a'+1;        sa.s[sa.n++]=0;        sa.build_sa(30);        sa.build_height();//求反串的最大字典序位置时候需要借助height数组        int k=sa.n-1;        while(k>=1&&sa.height[k]&&sa.height[k]==n-sa.sa[k-1])k--; //LCP和k-1处的串长相等时候往前移动        p1=sa.sa[k];    }}int main(){    int T;    scanf("%d",&T);    while(T--)    {        scanf("%d",&n);        scanf("%s",word);        for(int i=0;i<n;i++)            rev[i]=word[n-1-i];        solve(0);        solve(1);        int ans,dir;        int d=cmp(word,rev,p0,p1);        if(d==2)        {            if(p0<=n-1-p1)ans=p0,dir=0; //反串中的p1在正串中的下标是n-1-p1            else ans=n-1-p1,dir=1;        }        else if(d>0)ans=p0,dir=0;        else ans=n-1-p1,dir=1;        printf("%d %d\n",ans+1,dir);    }}


0 0
原创粉丝点击