题链:
http://poj.org/problem?id=1509
题解:
给出一个字符串,有一个操作:把首字符放到末尾,形成新的串。
求任意次操作后,字典序最小的串的首字母在原串中的位置。
(这就是最小表示法?哈)
把原串翻倍,建立后缀自动机。
然后在自动机上从起点往当前节点的较小的字母上跑len步即可。
代码:
#include<cstdio> #include<cstring> #include<iostream> #define MAXN 40050 #define filein(x) freopen(#x".in","r",stdin); #define fileout(x) freopen(#x".out","w",stdout); using namespace std; struct SAM{ int size,last,p,q,np,nq; int step[MAXN],pre[MAXN],ch[MAXN][26]; int newnode(int a,int b){ step[size]=a; memcpy(ch[size],ch[b],sizeof(ch[b])); return size++; } void add(int x){ p=last; last=np=newnode(step[p]+1,0); while(p&&!ch[p][x]) ch[p][x]=np,p=pre[p]; if(!p) pre[np]=1; else{ q=ch[p][x]; if(step[p]+1!=step[q]){ nq=newnode(step[p]+1,q); pre[nq]=pre[q]; pre[q]=pre[np]=nq; while(p&&ch[p][x]==q) ch[p][x]=nq,p=pre[p]; } else pre[np]=q; } } void build(char *S){ memset(ch[0],0,sizeof(ch[0])); size=1; last=newnode(-1,0); for(int i=0;S[i];i++) add(S[i]-'a'); } }suf; int find(int len){ int p=1; for(int i=0;i<len;i++) for(int j=0;j<26;j++) if(suf.ch[p][j]){ p=suf.ch[p][j]; break; } return suf.step[p]-len+1; } int main() { int N,len; char S[MAXN]; scanf("%d",&N); for(int cas=1,ans;cas<=N;cas++){ scanf("%s",S); len=strlen(S); for(int i=0;i<len;i++) S[len+i]=S[i]; S[2*len]=0; suf.build(S); ans=find(len)+1; printf("%d ",ans); } return 0; }