题意:给出一个圆圈,圆圈上顺时针有一些字符,现在要从某个字符前边断开,得到一个以这个字符开头的字符串,使得得到的字符串字典序最小。如果多解,输出最小的断点。
题解:把字符串copy一次,放到原串后边,使得长度*2,然后放到SAM中,从Root开始按照字典序走贪心len步(如果不能走就提前break) 搞定。
Code:
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1e4+100;
char s[maxn];
int n;
int L;
struct SAM{
int last,cnt,nxt[maxn*2][26],fa[maxn*2],len[maxn*2];
void init(){
last=cnt=1;
memset(nxt[1],0,sizeof nxt[1]);
fa[1]=len[1]=0;
}
int newnode(){
++cnt;
memset(nxt[cnt],0,sizeof nxt[cnt]);
fa[cnt]=len[cnt]=0;
return cnt;
}
void add(int c){
int p=last;
int np = newnode();
last = np;
len[np]=len[p]+1;
while (p&&!nxt[p][c]){
nxt[p][c]=np;
p=fa[p];
}
if (!p){
fa[np]=1;
}else{
int q = nxt[p][c];
if (len[q]==len[p]+1){
fa[np]=q;
}else{
int nq=newnode();
len[nq]=len[p]+1;
memcpy(nxt[nq],nxt[q],sizeof (nxt[q]));
fa[nq]=fa[q];
fa[np]=fa[q]=nq;
while (nxt[p][c]==q){
nxt[p][c]=nq;
p=fa[p];
}
}
}
}
int query(){
int now =1;
for (int i=0;i<L;i++){
bool flag = false;
for (int j=0;j<=25;j++){
if (nxt[now][j]){
flag = true;
now = nxt[now][j];
break;
}
}
if (!flag){
break;
}
}
return len[now]+1-L;
}
}sam;
int main(){
scanf("%d",&n);
while (n--){
sam.init();
scanf("%s",s);
L = strlen(s);
for (int i=0;i<L;i++){
sam.add(s[i]-'a');
}
for (int i=0;i<L;i++){
sam.add(s[i]-'a');
}
printf("%d\n",sam.query());
}
return 0;
}