HDU 5442 后缀自动机(从环字符串选定一个位置 , 时针或顺时针走一遍,希望得到字典序最大)

http://acm.hdu.edu.cn/showproblem.php?pid=5442

题目大意:

给定一个字符串,可理解成环,然后选定一位置,逆时针或顺时针走一遍,希望得到字典序最大,如果同样大,希望找到起始位置最小的,如果还相同,就默认顺时针

后缀自动机上s记录达到的最长的位置,如果不更新,那么必然一次跑完得到的是最小位置,那么为了得到最大,之前更新每一个节点中的s,只有儿子位置上的能更新父亲

所以先将后缀自动机拓扑排序,然后从后往前,不断将父亲的s更新成更大的s

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
#define M 26
#define N 80100
#define ull unsigned long long
char str[N] , tmp[N];
int n , num[N];
queue<int> Q;

struct SamNode{
    SamNode *son[M] , *f;
    int l , s;
    void init(){
        for(int i=0 ; i<M ; i++) son[i] = NULL;
        f = NULL;
        l = s = 0;
    }
}*b[N];

SamNode sam[N] , *root , *last;
int cnt;

void init(){
    cnt = 0;
    sam[0].init();
    root = last = &sam[0];
}

void add(int x){
    sam[cnt+1].init();
    SamNode *p = &sam[++cnt] , *jp=last;
    /*
    这里p->s = jp->s+1写成p->s = p->l也是一样的,因为对于每次当前的last来说,
    l和s的值是一样的,因为每次当前的last都是处于字符串的位置上的
    数,不是额外添加的节点
    */
    p->l = jp->l+1; p->s = jp->s+1;
    last = p;
    for(; jp&&!jp->son[x] ; jp=jp->f) jp->son[x] = p;
    if(!jp) p->f=root;
    else{
        if(jp->l+1 == jp->son[x]->l) p->f = jp->son[x];
        else{
            sam[cnt+1].init();
            SamNode *r = &sam[++cnt] , *q = jp->son[x];
            *r=*q;
            r->l = jp->l+1 ; r->s = p->l;
            q->f = p->f = r;
            for(; jp && jp->son[x]==q ; jp=jp->f) jp->son[x]=r;
        }
    }
}

int solve(int len)
{
    SamNode *cur = root;
    for(int i=0 ; i<len ; i++){
        for(int j=25 ; j>=0 ; j--){
            if(cur->son[j]){
                cur = cur->son[j];
                break;
            }
        }
    }
    int ret = cur->s-len+1;

    return ret;
}

int solve1(int len)
{
    memset(num , 0 , sizeof(num));
    for(int i=1 ; i<=cnt ; i++) num[sam[i].l]++;
    for(int i=1 ; i<=len*2 ; i++) num[i] += num[i-1];
    for(int i=1 ; i<=cnt ; i++) b[--num[sam[i].l]] = &sam[i];
    for(int i=cnt-1 ; i>=0 ; i--){
        b[i]->f->s = max(b[i]->f->s , b[i]->s);
    }
    SamNode *cur = root;
    for(int i=0 ; i<len ; i++){
        for(int j=25 ; j>=0 ; j--){
            if(cur->son[j]){
                cur = cur->son[j];
                break;
            }
        }
    }
    int ret = cur->s-len+1;
    return ret;
}

int main() {
 //  freopen("a.in" , "r" , stdin);
//    freopen("out.txt" , "w" , stdout);
    int T;
    scanf("%d" , &T);
    while(T--){
        scanf("%d" , &n);
        scanf("%s" , str);
        int len = n;
        init();
        for(int i=0 ; i<len ; i++)
            str[len+i] = str[i];
        for(int i=0 ; i<len*2 ; i++)
            add(str[i]-'a');
      //  for(int i=0 ; i<2*n ; i++) cout<<str[i];
        //cout<<endl;
        int ret1 = solve(len);

        for(int i=0 ; i<len ; i++){
            tmp[len-i-1] = str[i];
        }
        for(int i=0 ; i<len ; i++){
            tmp[len+i] = tmp[i];
        }

        init();
        for(int i=0 ; i<len*2-1 ; i++)
            add(tmp[i]-'a');
        int ret2 = len-solve1(len)+1;

       //  cout<<"here: "<<ret1<<" "<<ret2<<endl;
      //  cout<<ret1<<" "<<ret2<<endl;
        int i , j , cnt , pos , wise=-1;
        for(i=ret1-1 , j=ret2+len-1 , cnt=0 ; cnt<len ; cnt++ , i++ , j--){
            //    cout<<cnt<<" "<<i<<" "<<j<<" "<<str[i]<<" "<<str[j]<<endl;
            if(str[i]>str[j]){wise=0;pos=ret1;break;}
            else if(str[i]<str[j]){wise=1;pos=ret2;break;}
        }
        if(wise==-1){
            if(ret1<ret2) pos = ret1 , wise = 0;
            else if(ret1>ret2) pos = ret2 , wise = 1;
            else pos=ret1 , wise=0;
         //   cout<<"in: "<<endl;
        }
        printf("%d %d\n" , pos, wise);
    }
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/shuaihui520/p/10693226.html