字符串最小表示 后缀自动机 O(n)
把串复制一次,链接在后面之后,建立SAM,贪心地在SAM上转移,每次贪心地选择最小的字符,转移的长度为n时停止。
输出时由于要最靠前的,所以要在endpos集合中挑一个最小的,这个在slink_tree上递推一下就能轻松获得。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define MAXL 10000 #define MAXC 26 int v[2*MAXL+10],__next[2*MAXL+10],first[2*MAXL+10],e; void AddEdge(int U,int V){ v[++e]=V; __next[e]=first[U]; first[U]=e; } char s[MAXL*2+10];//文本串 int len/*文本串长度*/; struct SAM{ int minendpos[2*MAXL+10]; int n/*状态数0~n-1*/,maxlen[2*MAXL+10],minlen[2*MAXL+10],trans[2*MAXL+10][MAXC],slink[2*MAXL+10]; void clear(){ memset(maxlen,0,sizeof(maxlen)); memset(minlen,0,sizeof(minlen)); memset(trans,0,sizeof(trans)); memset(slink,0,sizeof(slink)); memset(minendpos,0x7f,sizeof(minendpos)); n=0; } int new_state(int _maxlen,int _minlen,int _trans[],int _slink){ maxlen[n]=_maxlen; minlen[n]=_minlen; for(int i=0;i<MAXC;++i){ if(_trans==NULL){ trans[n][i]=-1; } else{ trans[n][i]=_trans[i]; } } slink[n]=_slink; return n++; } int add_char(char ch,int u,int pos){ if(u==-1){ return new_state(0,0,NULL,-1); } int c=ch-'a'; int z=new_state(maxlen[u]+1,-1,NULL,-1); minendpos[z]=pos; int v=u; while(v!=-1 && trans[v][c]==-1){ trans[v][c]=z; v=slink[v]; } if(v==-1){//最简单的情况,suffix-path(u->S)上都没有对应字符ch的转移 minlen[z]=1; slink[z]=0; return z; } int x=trans[v][c]; if(maxlen[v]+1==maxlen[x]){//较简单的情况,不用拆分x minlen[z]=maxlen[x]+1; slink[z]=x; return z; } int y=new_state(maxlen[v]+1,-1,trans[x],slink[x]);//最复杂的情况,拆分x slink[y]=slink[x]; minlen[x]=maxlen[y]+1; slink[x]=y; minlen[z]=maxlen[y]+1; slink[z]=y; int w=v; while(w!=-1 && trans[w][c]==x){ trans[w][c]=y; w=slink[w]; } minlen[y]=maxlen[slink[y]]+1; return z; } void dfs(int U){ for(int i=first[U];i;i=__next[i]){ dfs(v[i]); minendpos[U]=min(minendpos[U],minendpos[v[i]]); } } void work_slink_tree(){ for(int i=1;i<n;++i){ AddEdge(slink[i],i); } dfs(0); } }sam; int T; int main(){ // freopen("poj1509.in","r",stdin); scanf("%d",&T); for(;T;--T){ sam.clear(); scanf("%s",s); len=strlen(s); for(int i=0;i<len-1;++i){ s[i+len]=s[i]; } int U=sam.add_char(0,-1,0); for(int i=0;i<len*2-1;++i){ U=sam.add_char(s[i],U,i); } U=0; for(int i=0;i<len;++i){ for(int j=0;j<MAXC;++j){ if(sam.trans[U][j]!=-1){ U=sam.trans[U][j]; break; } } } printf("%d ",sam.minendpos[U]-len+2); } return 0; }