先考慮假設我們把所有後綴建成一個Trie樹,那麼一個後綴字典序能成爲最大就會帶來一些限制,具體地就是從根出發遍歷這個串,遍歷的邊上的字母映射的值要大於其他邊上的字母。我們只要對於每個後綴帶來的限制判一下環即可,複雜度 ,其中 爲字符集大小。
可以利用後綴樹的性質優化上面的操作,也就是把只有一條出邊的點壓縮起來,其中第一條邊的字母充當壓縮後的邊上的字母即可。因爲SAM的節點數是 的,所以複雜度降到 。
代碼:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010
#define C 12
using namespace std;
char s[N];
int tote,n,mp[C][C],ans[N],con[N<<1],to[N<<1],nxt[N<<1],w[N<<1],tim;
bool vis[C],insta[C];
void ins(int x,int y,int z)
{
to[++tote]=y;
w[tote]=z;
nxt[tote]=con[x];
con[x]=tote;
}
struct sam
{
int last,cnt,fa[N<<1],a[N<<1][C],mx[N<<1],pos[N<<1];
void clr()
{
for(int i=1;i<=cnt;i++)
{
fa[i]=mx[i]=pos[i]=0;
for(int c=0;c<C;c++)
a[i][c]=0;
}
last=cnt=1;
}
void extend(int c,int x)
{
int p=last,np=++cnt;last=np;
mx[np]=mx[p]+1;pos[np]=x;
for(;p&&!a[p][c];p=fa[p]) a[p][c]=np;
if(!p) fa[np]=1;
else
{
int q=a[p][c];
if(mx[q]==mx[p]+1) fa[np]=q;
else
{
int nq=++cnt;mx[nq]=mx[p]+1;pos[nq]=pos[q];
for(int ic=0;ic<C;ic++)
a[nq][ic]=a[q][ic];
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
for(;p&&a[p][c]==q;p=fa[p]) a[p][c]=nq;
}
}
}
}T;
bool dfs(int v)
{
vis[v]=1;insta[v]=1;
bool re=0;
for(int c=0;c<C&&!re;c++)
if(mp[v][c])
{
if(insta[c]) {return 1;}
if(!vis[c]) re=re|dfs(c);
}
insta[v]=0;
return re;
}
void walk(int x)
{
bool fff=0;
for(int p=con[x];p;p=nxt[p])
{
for(int q=con[x];q;q=nxt[q])
if(w[p]!=w[q]) mp[w[p]][w[q]]++;
walk(to[p]);
for(int q=con[x];q;q=nxt[q])
if(w[p]!=w[q]) mp[w[p]][w[q]]--;
fff=1;
}
if(!fff)
{
int rd=0;bool flag=1;
memset(vis,0,sizeof(vis));
memset(insta,0,sizeof(insta));
tim=0;
for(int c=0;c<C;c++)
if(!vis[c])
if(dfs(c)) {flag=0;break;}
ans[n-T.pos[x]+1]=flag;
}
}
int main()
{
int ca;
scanf("%d",&ca);
while(ca--)
{
for(int i=1;i<=n;i++)
ans[i]=0;
for(int i=1;i<=T.cnt;i++)
con[i]=0;
for(int i=1;i<=tote;i++)
to[i]=nxt[i]=0;
tote=0;
T.clr();
scanf("%s",s+1);
n=strlen(s+1);
reverse(s+1,s+n+1);
for(int i=1;i<=n;i++)
T.extend(s[i]-'a',i);
for(int i=2;i<=T.cnt;i++)
ins(T.fa[i],i,s[T.pos[i]-T.mx[T.fa[i]]]-'a');
walk(1);
for(int i=1;i<=n;i++)
putchar('0'+ans[i]);
puts("");
}
return 0;
}