题意
给定n,求最小循环子串的开头位置。
若有多个答案,输出开头位置最小的。
n<=10000
题解
SAM
最水的模板题。
把串复制一段接在后面,然后在SAM上走,每次选标号最小的边走,走n步就达到了答案状态。
我们还需要输出位置。就相当于求子串在原串中首次出现位置,这个问题我们可以对每个状态维护
Right
集合的最小元素
Firpos
来解决。
在维护时,显然之前的状态的
Firpos
是不会变得更小的。
新建
np
时,
Firpos(np)=Firpos(last)+1
.
新建
nq
时,
Firpos(nq)=Firpos(q)
. 都很显然。
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=20005;
struct node{
node *par,*ch[26]; int _max,firpos;
} *root, *last, base[850000], *p_top=base;
typedef node* P_node;
int res;
P_node newnode(int t1=0){
p_top->par=0; p_top->_max=p_top->firpos=t1;
memset(p_top->ch,0,sizeof(p_top->ch));
return p_top++;
}
void Extend(char x){
P_node p=last, np=newnode(p->_max+1);
while(p&&p->ch[x]==0) p->ch[x]=np, p=p->par;
if(!p) np->par=root; else{
P_node q=p->ch[x];
if(q->_max==p->_max+1) np->par=q; else{
P_node nq=newnode(p->_max+1); nq->firpos=q->firpos;
memcpy(nq->ch,q->ch,sizeof(q->ch));
nq->par=q->par; q->par=nq; np->par=nq;
while(p&&p->ch[x]==q) p->ch[x]=nq, p=p->par;
}
}
last=np;
}
int _test,n;
char s[maxn];
int main(){
freopen("poj1509.in","r",stdin);
freopen("poj1509.out","w",stdout);
scanf("%d",&_test);
while(_test--){
scanf("%s",s+1); n=strlen(s+1);
for(int i=1;i<=n;i++) s[i+n]=s[i];
p_top=base; last=root=newnode(0);
for(int i=1;i<=n*2;i++) Extend(s[i]-'a');
P_node p=root;
for(int i=1;i<=n;i++){
for(int j=0;j<=25;j++) if(p->ch[j]){ p=p->ch[j]; break; }
}
printf("%d\n",p->firpos-n+1);
}
return 0;
}