Problem
话说这是一道5倍经验题?(poj,zoj,uva,uvalive,spoj)
题意:给定一个环形字符串 ,求从哪一位作为起始字符整个字符串的字典序最小
Solution
这题有多种解法,蒻为了学习便用了后缀自动机
这题要求字符串的一种形态下字典序最小,明显可以贪心:提取出字典序最小的字符,再从这些字符的后面取出次小字符……直到只剩一种情况
这样虽说在随机数据下可以A,但极限数据比如:aaa……aaa便可以卡成 ,倒也能过,只不过数据开到 就gg了
发现后缀自动机存下了所有的子串,而在自动机上走len步就为一种合法答案,而求所有合法答案中字典序最小,就可以直接贪心了:按套路先将字符串倍增,再每次取最小转移,转移len次,由于最终到达的位置一定为倍增的后一半,所以在原串中的位置为
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;
}