题面描述
You are given a string S consisting of only lowercase english letters and some queries.
For each query
(
l
,
r
,
k
)
(l,r,k)
(l,r,k), please output the starting position of the
k
−
t
h
k-th
k−th occurence of the substring
S
l
S
l
+
1
.
.
.
S
r
S_lS_{l+1}...S_r
SlSl+1...Sr in
S
S
S.
输入格式
The first line contains an integer
T
(
1
≤
T
≤
20
)
T(1≤T≤20)
T(1≤T≤20), denoting the number of test cases. The first line of each test case contains two integer
N
(
1
≤
N
≤
1
0
5
)
,
Q
(
1
≤
Q
≤
1
0
5
)
N(1≤N≤10^5),Q(1≤Q≤10^5)
N(1≤N≤105),Q(1≤Q≤105), denoting the length of
S
S
S and the number of queries. The second line of each test case contains a string
S
(
∣
S
∣
=
N
)
S(|S|=N)
S(∣S∣=N) consisting of only lowercase english letters. Then Q lines follow, each line contains three integer
l
,
r
(
1
≤
l
≤
r
≤
N
)
l,r(1≤l≤r≤N)
l,r(1≤l≤r≤N) and
k
(
1
≤
k
≤
N
)
k(1≤k≤N)
k(1≤k≤N), denoting a query. There are at most
5
5
5 testcases which
N
N
N is greater than
1
0
3
10^3
103.
输入样例
2 12 6 aaabaabaaaab 3 3 4 2 3 2 7 8 3 3 4 2 1 4 2 8 12 1 1 1 a 1 1 1
输出样例
5 2 -1 6 9 8 1
题面分析
给一个长度为
n
n
n的串,有
q
q
q次询问,每次给一个三元组
(
l
,
r
,
k
)
(l,r,k)
(l,r,k)求S[l,r]出现第k次的位置。 因为
n
n
n很大,对于某个串出现多次的位置判断等可以用后缀数组处理,如果
S
[
l
,
n
]
S[l,n]
S[l,n]和其他任意一个串
q
[
x
,
n
]
q[x,n]
q[x,n]的LCP
≥
\geq
≥r-l+1,则在
q
q
q中S[l,r]出现过。 因为
q
q
q是有序的,所以我们需要找到符合题意的
q
q
q的范围。 注意到Rank[i]的后缀与其他后缀的LCP随着排名之差绝对值增大而减小,因此我们考虑二分Rank得到满足条件的
q
q
q的Rank上下界
a
n
s
L
,
a
n
s
R
ansL,ansR
ansL,ansR。 于是接下来问题变成了如何第k次的下标,下标位置就是SA数组,考虑根据SA数组建立一颗可持久化线段树,然后每次询问
[
a
n
s
L
,
a
n
s
R
]
[ansL,ansR]
[ansL,ansR]内的第k个即可。
AC代码(1400ms)
#include <bits/stdc++.h>
using namespace std
;
const int maxn
=3e5;
#define ll long long
namespace SA
{
int SA
[maxn
+10];
int Rank
[maxn
+10];
int Height
[maxn
+10];
int tax
[maxn
+10], tp
[maxn
+10];
int len
;
int s
[maxn
+10];
int m
;
void RSort()
{
for(int i
=0; i
<=m
; i
++) tax
[i
]=0;
for(int i
=1; i
<=len
; i
++) tax
[Rank
[tp
[i
]]]++;
for(int i
=1; i
<=m
; i
++) tax
[i
]+=tax
[i
-1];
for(int i
=len
; i
>=1; i
--) SA
[tax
[Rank
[tp
[i
]]]--]=tp
[i
];
}
int cmp(int *f
, int x
, int y
, int w
)
{
return f
[x
]==f
[y
] && f
[x
+w
]==f
[y
+w
];
}
void GetSA()
{
for(int i
=1; i
<=len
; i
++)
{
Rank
[i
]=s
[i
], tp
[i
]=i
;
}
m
=200;
RSort();
for(int w
=1, p
=1, i
; p
<len
; w
+=w
, m
=p
)
{
for(p
=0, i
=len
-w
+1; i
<=len
; i
++)
tp
[++p
]=i
;
for(i
=1; i
<=len
; i
++)
if (SA
[i
]>w
)
tp
[++p
]=SA
[i
]-w
;
RSort(), swap(Rank
, tp
), Rank
[SA
[1]]=p
=1;
for(i
=2; i
<=len
; i
++)
Rank
[SA
[i
]]=cmp(tp
, SA
[i
], SA
[i
-1], w
) ? p
: ++p
;
}
}
void GetHeight()
{
int j
, k
=0;
for(int i
=1; i
<=len
; Height
[Rank
[i
++]]=k
)
for(k
=k
? k
-1 : k
, j
=SA
[Rank
[i
]-1]; s
[i
+k
]==s
[j
+k
]; ++k
);
}
void PrintSA()
{
for(int i
=1; i
<=len
; i
++)
{
printf("%d ", SA
[i
]);
}
printf("\n");
}
void PrintHeight()
{
for(int i
=1; i
<=len
; i
++)
{
printf("%d ", Height
[i
]);
}
printf("\n");
}
void PrintRank()
{
for(int i
=1; i
<=len
; i
++)
{
printf("%d ", Rank
[i
]);
}
printf("\n");
}
}
namespace ST
{
const int logn
=30;
int f
[maxn
+10][logn
], Log
[maxn
+10];
void init_log()
{
Log
[0]=-1;
Log
[1]=0;
Log
[2]=1;
for(int i
=3; i
<maxn
; i
++)
{
Log
[i
]=Log
[i
/2]+1;
}
}
void build(int n
, int *a
)
{
for(int i
=1; i
<=n
; i
++)
{
f
[i
][0]=a
[i
];
}
init_log();
for(int j
=1; j
<=logn
; j
++)
{
for(int i
=1; i
+(1<<j
)-1<=n
; i
++)
{
f
[i
][j
]=min(f
[i
][j
-1], f
[i
+(1<<(j
-1))][j
-1]);
}
}
}
int query(int l
, int r
)
{
if (l
==r
)
return SA
::len
-SA
::SA
[l
];
if (l
>r
)
swap(l
, r
);
l
++;
int s
=Log
[r
-l
+1];
return min(f
[l
][s
], f
[r
-(1<<s
)+1][s
]);
}
}
namespace President_Tree
{
int cnt
=0;
const int Mul
=40;
int T
[maxn
+10];
int sum
[maxn
*Mul
+10], L
[maxn
*Mul
+10], R
[maxn
*Mul
+10];
inline int President_Tree_init()
{
cnt
=0;
memset(L
, 0, sizeof(L
));
memset(R
, 0, sizeof(R
));
memset(sum
, 0, sizeof(sum
));
memset(T
, 0, sizeof(T
));
}
inline int build(int l
, int r
)
{
int rt
=++cnt
;
sum
[rt
]=0;
if (l
<r
)
{
int mid
=(l
+r
)>>1;
L
[rt
]=build(l
, mid
);
R
[rt
]=build(mid
+1, r
);
}
return rt
;
}
inline int update(int pre
, int l
, int r
, int x
)
{
int rt
=++cnt
;
L
[rt
]=L
[pre
];
R
[rt
]=R
[pre
];
sum
[rt
]=sum
[pre
]+1;
if (l
<r
)
{
int mid
=(l
+r
)>>1;
if (x
<=mid
) L
[rt
]=update(L
[pre
], l
, mid
, x
);
else R
[rt
]=update(R
[pre
], mid
+1, r
, x
);
}
return rt
;
}
inline int query(int u
, int v
, int l
, int r
, int k
)
{
if (l
>=r
) return l
;
int x
=sum
[L
[v
]]-sum
[L
[u
]];
int mid
=(l
+r
)>>1;
if (x
>=k
)
return query(L
[u
], L
[v
], l
, mid
, k
);
return query(R
[u
], R
[v
], mid
+1, r
, k
-x
);
}
}
char a
[maxn
+10];
int b
[maxn
+10];
void solve()
{
int t
;
scanf("%d", &t
);
while(t
--)
{
int len
, q
;
scanf("%d%d", &len
, &q
);
scanf("%s", a
+1);
for(int i
=1; i
<=len
; i
++)
{
SA
::s
[i
]=a
[i
]-'a'+1;
}
SA
::len
=len
;
SA
::s
[len
+1]=0;
SA
::GetSA();
SA
::GetHeight();
ST
::build(len
, SA
::Height
);
for(int i
=1; i
<=SA
::len
; i
++)
{
b
[i
]=SA
::SA
[i
];
}
sort(b
+1, b
+SA
::len
+1);
int cntb
=unique(b
+1, b
+SA
::len
+1)-(b
+1);
President_Tree
::T
[0]=President_Tree
::build(1, cntb
);
for(int i
=1; i
<=SA
::len
; i
++)
{
int index
=lower_bound(b
+1, b
+SA
::len
+1, SA
::SA
[i
])-b
;
President_Tree
::T
[i
]=President_Tree
::update(President_Tree
::T
[i
-1], 1, cntb
, index
);
}
while(q
--)
{
int l
, r
, k
;
scanf("%d%d%d", &l
, &r
, &k
);
int L
=1, R
=SA
::Rank
[l
]-1;
int ansL
=SA
::Rank
[l
], ansR
=SA
::Rank
[l
];
while(L
<=R
)
{
int mid
=(L
+R
)>>1;
if (ST
::query(mid
, SA
::Rank
[l
])>=r
-l
+1)
{
ansL
=mid
;
R
=mid
-1;
}
else
{
L
=mid
+1;
}
}
L
=SA
::Rank
[l
]+1, R
=len
;
while(L
<=R
)
{
int mid
=(L
+R
)>>1;
if (ST
::query(SA
::Rank
[l
], mid
)>=r
-l
+1)
{
ansR
=mid
;
L
=mid
+1;
}
else
{
R
=mid
-1;
}
}
if (ansR
-ansL
+1<k
)
{
printf("-1\n");
continue;
}
printf("%d\n", President_Tree
::query(President_Tree
::T
[ansL
-1], President_Tree
::T
[R
], 1, cntb
, k
));
}
}
}
int main()
{
#ifdef ACM_LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
long long test_index_for_debug
=1;
char acm_local_for_debug
;
while(cin
>>acm_local_for_debug
)
{
cin
.putback(acm_local_for_debug
);
if (test_index_for_debug
>100)
{
throw runtime_error("Check the stdin!!!");
}
auto start_clock_for_debug
=clock();
solve();
auto end_clock_for_debug
=clock();
cout
<<"\nTest "<<test_index_for_debug
<<" successful"<<endl
;
cerr
<<"Test "<<test_index_for_debug
++<<" Run Time: "
<<double(end_clock_for_debug
-start_clock_for_debug
)/CLOCKS_PER_SEC
<<"s"<<endl
;
cout
<<"--------------------------------------------------"<<endl
;
}
#else
solve();
#endif
return 0;
}
后记
综合性很强的题,特此记录。 DrGilbert 2020.10.22