【题解】poj-1905 Glass Beads

Problem

话说这是一道5倍经验题?(poj,zoj,uva,uvalive,spoj)

题意:给定一个环形字符串 ( l e n 10 4 ) ,求从哪一位作为起始字符整个字符串的字典序最小

Solution

这题有多种解法,蒻为了学习便用了后缀自动机

这题要求字符串的一种形态下字典序最小,明显可以贪心:提取出字典序最小的字符,再从这些字符的后面取出次小字符……直到只剩一种情况

这样虽说在随机数据下可以A,但极限数据比如:aaa……aaa便可以卡成 O ( n 2 ) ,倒也能过,只不过数据开到 10 6 就gg了

发现后缀自动机存下了所有的子串,而在自动机上走len步就为一种合法答案,而求所有合法答案中字典序最小,就可以直接贪心了:按套路先将字符串倍增,再每次取最小转移,转移len次,由于最终到达的位置一定为倍增的后一半,所以在原串中的位置为 s t p [ x ] l e n + 1

Code

核心贪心代码:

for(rg int i=0;i<n;++i)
for(rg int j=0;j<26;++j)
    if(ch[e][j]){e=ch[e][j];break;}
printf("%d\n",stp[e]-n+1);

整体代码:

//poj-1509
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long ll;
#define rg register
#define cl(x) memset(x,0,sizeof(x))

const int N=50010;
int ch[N][26],pre[N],stp[N];
char s[N];
int n,tot,last;

inline void cln(int x){cl(ch[x]);pre[x]=stp[x]=0;}

inline void ins(int x){
    rg int p=last,np=++tot;cln(tot);
    last=np,stp[np]=stp[p]+1;
    while(p&&!ch[p][x])ch[p][x]=np,p=pre[p];
    if(!p)pre[np]=1;
    else {
        rg int q=ch[p][x];
        if(stp[q]==stp[p]+1)pre[np]=q;
        else {
            int nq=++tot;cln(tot);stp[nq]=stp[p]+1;
            for(rg int i=0;i<26;++i)ch[nq][i]=ch[q][i];
            pre[nq]=pre[q],pre[q]=pre[np]=nq;
            while(ch[p][x]==q)ch[p][x]=nq,p=pre[p];
        }
    }
    return ;
}

int main(){
    int T;scanf("%d\n",&T);
    while(T--){
        tot=last=1;cln(1);
        scanf("%s",s);n=strlen(s);
        for(rg int i=0;i<n;++i)
        ins(s[i]-'a');
        for(rg int i=0;i<n;++i)ins(s[i]-'a');
        int e=1;
        for(rg int i=0;i<n;++i)
        for(rg int j=0;j<26;++j)
            if(ch[e][j]){e=ch[e][j];break;}
        printf("%d\n",stp[e]-n+1);
    }return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40515553/article/details/80150210