题目
正解
辣鸡数据结构题。
考虑某个串出现的所有位置的右端点。设相邻位置之差为
d
i
d_i
di(
d
1
=
+
∞
d_1=+\infty
d1=+∞),于是它覆盖的位置为
∑
m
i
n
{
d
i
,
l
e
n
}
\sum min\{d_i,len\}
∑min{di,len},也就是
∑
d
i
≤
l
e
n
d
i
+
l
e
n
∑
[
d
i
>
l
e
n
]
\sum_{d_i\leq len}d_i+len\sum [d_i>len]
∑di≤lendi+len∑[di>len]
于是考虑维护值在某个区间内的
d
i
d_i
di的和、个数。(树状数组)
建
S
A
M
SAM
SAM,建出
f
a
i
l
fail
fail树,对于每个节点,如果我们得到了它的
r
i
g
h
t
right
right集合,二分
l
e
n
len
len,试着找到覆盖位置恰好为
k
k
k的字符串。
接下来就是维护
r
i
g
h
t
right
right集合和树状数组。
直接上
d
s
u
o
n
t
r
e
e
dsu\ on \ tree
dsu on tree,用
s
e
t
set
set维护
r
i
g
h
t
right
right集合。
至于最终怎么比较字符串的大小,可以直接字符串哈希二分;或者一开始建反串,这样跑
S
A
M
SAM
SAM之后建出的
f
a
i
l
fail
fail树就是后缀树(记得钦定
f
a
i
l
fail
fail边的顺序,通过位置
s
r
−
f
a
i
l
m
a
x
l
e
n
s_{r-fail_{maxlen}}
sr−failmaxlen来比较大小,其中
r
r
r是
r
i
g
h
t
right
right集合中随便一个点)。
时间复杂度
O
(
n
lg
2
n
)
O(n\lg^2 n)
O(nlg2n)
代码
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
#define N 40010
#define ll long long
int n,k;
char str[N];
struct Node{
Node *c[26],*fail;
int len;
} d[N];
int cnt;
Node *S,*T;
Node *newnode(){
Node *nw=&d[++cnt];
memset(nw,0,sizeof(Node));
return nw;
}
void insert(char ch){
ch-='a';
Node *nw=newnode();
nw->len=T->len+1;
Node *p=T;
for (;p && !p->c[ch];p=p->fail)
p->c[ch]=nw;
if (!p)
nw->fail=S;
else{
Node *q=p->c[ch];
if (q->len==p->len+1)
nw->fail=q;
else{
Node *clone=newnode();
memcpy(clone,q,sizeof(Node));
clone->len=p->len+1;
for (;p && p->c[ch]==q;p=p->fail)
p->c[ch]=clone;
nw->fail=q->fail=clone;
}
}
T=nw;
}
struct EDGE{
int to;
EDGE *las;
} e[N];
int ne;
EDGE *last[N];
int fa[N];
int pos[N];
ll t0[N],t1[N];
void add(int x,int c,ll t[]){
for (;x<=n;x+=x&-x)
t[x]+=c;
}
ll query(int x,ll t[]){
ll r=0;
for (;x;x-=x&-x)
r+=t[x];
return r;
}
int siz[N],hs[N];
int minr[N];
void init(int x){
siz[x]=1,hs[x]=0;
minr[x]=(pos[x]!=-1?pos[x]:n+1);
for (EDGE *ei=last[x];ei;ei=ei->las){
init(ei->to);
siz[x]+=siz[ei->to];
if (siz[ei->to]>siz[hs[x]])
hs[x]=ei->to;
minr[x]=min(minr[x],minr[ei->to]);
}
}
set<int> s;
int f[N];
void addi(int i,int c){add(i,i*c,t1);add(i,c,t0);}
void geti(int i){
if (s.empty()){
addi(n,1);
s.insert(i);
return;
}
auto p=s.upper_bound(i);
if (p==s.end()){
--p;
addi(i-*p,1);
}
else{
if (p==s.begin()){
addi(*p-i,1);
}
else{
auto q=p;
--q;
addi(*p-*q,-1);
addi(*p-i,1),addi(i-*q,1);
}
}
s.insert(i);
}
void erasei(int i){
s.erase(i);
if (s.empty()){
addi(n,-1);
return;
}
auto p=s.upper_bound(i);
if (p==s.end()){
--p;
addi(i-*p,-1);
}
else{
if (p==s.begin()){
addi(*p-i,-1);
}
else{
auto q=p;
--q;
addi(*p-i,-1),addi(i-*q,-1);
addi(*p-*q,1);
}
}
}
int calc(int len){return query(len,t1)+(query(n,t0)-query(len,t0))*len;}
void insert(int x){
if (pos[x]!=-1)
geti(pos[x]);
for (EDGE *ei=last[x];ei;ei=ei->las)
insert(ei->to);
}
void clear(int x){
if (pos[x]!=-1)
erasei(pos[x]);
for (EDGE *ei=last[x];ei;ei=ei->las)
clear(ei->to);
}
void dfs(int x){
if (hs[x]){
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=hs[x]){
dfs(ei->to);
clear(ei->to);
}
dfs(hs[x]);
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=hs[x])
insert(ei->to);
}
if (x==1)
return;
if (pos[x]!=-1)
geti(pos[x]);
int l=d[x].fail->len+1,r=d[x].len,res=-1;
while (l<=r){
int mid=l+r>>1,tmp=calc(mid);
if (tmp==k){
res=mid;
break;
}
if (tmp>k)
r=mid-1;
else
l=mid+1;
}
f[x]=res;
}
const int mo1=1300000003;
const int mo2=1000000009;
const int mo3=1000000007;
ll pro1[N],pro2[N],pro3[N];
ll pw1[N],pw2[N],pw3[N];
bool comp(int x,int n,int y,int m){
if (n!=m)
return n<m;
int l=1,r=n,t=0;
while (l<=r){
int mid=l+r>>1;
if (((pro1[x+mid]-pro1[x]*pw1[mid])%mo1+mo1)%mo1==
((pro1[y+mid]-pro1[y]*pw1[mid])%mo1+mo1)%mo1 &&
((pro2[x+mid]-pro2[x]*pw2[mid])%mo2+mo2)%mo2==
((pro2[y+mid]-pro2[y]*pw2[mid])%mo2+mo2)%mo2 &&
((pro3[x+mid]-pro3[x]*pw3[mid])%mo3+mo3)%mo3==
((pro3[y+mid]-pro3[y]*pw3[mid])%mo3+mo3)%mo3)
l=(t=mid)+1;
else
r=mid-1;
}
return str[x+t]<str[y+t];
}
int main(){
freopen("that.in","r",stdin);
freopen("that.out","w",stdout);
int Q;
scanf("%d",&Q);
while (Q--){
scanf("%s%d",str,&k);
n=strlen(str);
pw1[0]=pw2[0]=pw3[0]=1;
for (int i=1;i<=n;++i){
pw1[i]=pw1[i-1]*37%mo1;
pw2[i]=pw2[i-1]*43%mo2;
pw3[i]=pw3[i-1]*53%mo3;
}
pro1[0]=pro2[0]=pro3[0]=0;
for (int i=1;i<=n;++i){
pro1[i]=(pro1[i-1]*37+str[i-1]-'a')%mo1;
pro2[i]=(pro2[i-1]*43+str[i-1]-'a')%mo2;
pro3[i]=(pro3[i-1]*53+str[i-1]-'a')%mo3;
}
cnt=0;
memset(pos,255,sizeof pos);
T=S=newnode();
for (int i=0;i<n;++i){
insert(str[i]);
pos[T-d]=i;
}
ne=0;
memset(last,0,sizeof last);
for (int i=2;i<=cnt;++i){
fa[i]=d[i].fail-d;
e[ne]={i,last[fa[i]]};
last[fa[i]]=e+ne++;
}
init(1);
memset(t0,0,sizeof t0);
memset(t1,0,sizeof t1);
s.clear();
dfs(1);
int x=0,len=n+1;
for (int i=2;i<=cnt;++i)
if (f[i]!=-1){
int _x=minr[i]-f[i]+1,_len=f[i];
if (comp(_x,_len,x,len)){
x=_x,len=_len;
}
}
if (x==0 && len==n+1)
printf("NOTFOUND!\n");
else{
str[x+len]=0;
printf("%s\n",str+x);
}
}
return 0;
}