PKU 3623 Best Cow Line, Gold

题目要求每次从头或从尾取一个字母,要求最后的结果字典序最小。

本题可以用暴力过,设置两个指针i j,比较s[i] s[j],不同则选小的,相同则再比较s[i+1] s[j-1]。。。。依此类推。

另一种方法是采用后缀数组,将比较的过程优化。先将原来的串正向反向连接起来( abc变成abc#cba),然后利用后缀数组初始化,需要比较的时候利用RMQ来读取不同的字母。

 

#include <stdio.h>

#include <string.h>

#include <math.h>

#define max(a,b) (a>b?a:b)

 

const int N = 60100;

const int INF = 1<<30;

 

int n;

char s[N];

int cnt[N], mem[4][N], *rank, *nrank, *sa, *nsa, h[N];

int d[N][30];

 

void radix_sort()

{

    int i, k;

    rank = mem[0];

    nrank = mem[1];

    sa = mem[2];

    nsa = mem[3];

    memset( cnt, 0, sizeof(cnt));

    memset( mem, 0, sizeof(mem));

 

    for(i = 0; i < n; i++) cnt[s[i]]++;

    for(i = 1; i < 256; i++) cnt[i] += cnt[i-1];

    for(i = n-1; i >= 0; i--) sa[--cnt[s[i]]] = i;

    for(rank[0]=0, i=1; i < n; i++)

    {

       rank[sa[i]] = rank[sa[i-1]];

       if(s[sa[i]]!=s[sa[i-1]]) rank[sa[i]]++;

    }

 

    for(k = 1; k<n && rank[sa[n-1]] < n-1; k*=2)

    {

       for(i = 0; i < n; i++) cnt[rank[sa[i]]] = i+1;

       for(i = n-1; i >= 0; i--)

       {

           if(sa[i]-k>=0)

              nsa[--cnt[rank[sa[i]-k]]] = sa[i]-k;

       }

 

       for(i = n-k; i < n; i++)

           nsa[--cnt[rank[i]]] = i;

/*//

       for ( i=0; i<n; i++ )

           printf("%d ", nsa[i] );

       printf("/n" );

*/

       for(nrank[nsa[0]]=0, i=1; i < n; i++)

       {

           nrank[nsa[i]] = nrank[nsa[i-1]];

           if(rank[nsa[i]] != rank[nsa[i-1]]

           || rank[nsa[i]+k] != rank[nsa[i-1]+k])

              nrank[nsa[i]]++;

       }

       int *stemp = rank; rank = nrank; nrank = stemp;

       stemp = sa; sa = nsa; nsa = stemp;

    }

}

 

void initRMQ( int *D, int top)

{

    int i , j ,k,sk;

    for( i = 0 ; i < top ; i ++ )

       d[i][0] = i;

    for( j = 1 ,k=2; k <= top ; j ++,k <<= 1 )

    {

       for( i = 0 ; i < top ; i ++ )

       {

           sk = i+(k>>1) ;

           d[i][j] = d[i][j-1] ;

           if( sk < top && D[d[i][j]] >= D[d[sk][j-1]] )

              d[i][j] = d[sk][j-1] ;

       }     

    }

}

 

int RMQ( int *D , int i , int j )

{

    //int k = log22[j-i+1] ;

    int k = int(log(j-i+1.0)/log(2.0)) ;

    int k2 = (1 << k)-1;

    if( D[d[i][k]] < D[d[j-k2][k]] )

       return d[i][k];

    else

       return d[j-k2][k];

}

 

void get_lcp_rmq()

{

    int i, j, k;

    memset( h, 0, sizeof(h));

    for (j = 0, i = 0; i < n; i++)

    {

       if (rank[i] == 0)

           h[0] = j = 0;

       else

       {

           k = sa[rank[i]-1];

           for (j = max(j-1,0); s[i+j] == s[k+j]; j++);

           h[rank[i]] = j;

       }

    }

    initRMQ( h, n );

}

int ln = 0;

void check()

{

    if ( ln==80 )

    {

       printf("/n");

       ln = 0;

    }

    ln++;

}

int main ()

{

//  freopen("3623.in","r",stdin);

//  freopen("3623.txt","w",stdout);

    char tmp[100];

    int range;

    scanf("%d", &n );

    range = n;

    int i, j;

    for ( i=0; i<n; i++ )

    {

       scanf("%s", tmp );

       s[i] = s[2*n-i] = tmp[0];

    }

    s[n] = '#';

    n = 2*n+1;

    s[n++] = 0;

    radix_sort();

    get_lcp_rmq();

    i = 0; j=range-1;

    while ( i<=j )

    {

       if ( s[i]<s[j] )

       {

           check();

           printf("%c", s[i] );

           i++;

       }

        else

       {

           if ( s[i]>s[j] )

           {

              check();

              printf("%c", s[j] );

              j--;

           }

           else

           {

              int xa = rank[i];

              int xb = rank[2*range-j];

              int tt;

              if ( xa<xb )

                  tt = h[RMQ( h, xa+1, xb )];

              else

                  tt = h[RMQ( h, xb+1, xa )];

              if ( s[i+tt]<s[j-tt] )

              {

                  check();

                  printf("%c", s[i] );

                  i++;

              }

              else

              {

                  check();

                  printf("%c", s[j] );

                  j--;

              }

           }

       }

    }

    printf("/n" );

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值