单点时限: 2.0 sec
内存限制: 512 MB
现在Jack有一个只由只由小写字母组成的字符串S,现在他想进行下面的查询:
查询S中[l,r]子串第2次出现时第一个字母的位置。
T组输入
对于每组输入,第一行输入一个n,m代表S的长度和查询的次数。
接下来m行,每行输入一组l,r,代表查询的子串。
(1≤n,m≤100000,1≤l≤r≤n)
对于每组查询,输出子串l,r第2次出现的位置,如果没有出现,输出−1。
input
1
5 5
aaabcc
1 2
1 3
4 4
5 5
1 5
output
2
-1
-1
6
-1
暴力hash居然可以水过去,不过还有更优的解法,其实可以吧这个题变得更难一些,即查询第$k$次出现的位置。利用ST表维护下后缀排序后的公共长度的最小值,然后二分找出两端合法的位置,主席树维护下排序后的序列,这样我们就把问题转化为经点的主席树第K大,而本题的K就是2。
AC代码
#include
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pairpii;
const int N = (int) 1e5 * 2 + 11;
const int M = (int) 1e6 + 11;
const int MOD = (int) 1e9 + 7;
const int INF = (int) 0x3f3f3f3f;
const ll INFF = (ll) 0x3f3f3f3f3f3f3f3f;
/*-----------------------------------------------------------*/
int n, k;
int Rank[N + 1], sa[N + 1], height[N + 1], tmp[N + 1];
int MIN[N + 1][40];
bool cmp_sa(int i, int j){
if(Rank[i] != Rank[j]) return Rank[i] < Rank[j];
else {
int ri = i + 2 <= n ? Rank[i + 2] : -1;
int rj = j + 2 <= n ? Rank[j + 2] : -1;
return ri < rj;
}
}
void SA(string s){
n = s.size();
for(int i = 0; i <= n; i++){
sa[i] = i; Rank[i] = (i < n) ? s[i] : -1;
}
for(k = 1; k <= n; k <<= 1){
sort(sa, sa + n + 1, cmp_sa);
tmp[sa[0]] = 0;
for(int i = 1; i <= n; i++){
tmp[sa[i]] = tmp[sa[i - 1]] + (cmp_sa(sa[i - 1], sa[i]) ? 1 : 0);
}
for(int i = 0; i <= n; i++){
Rank[i] = tmp[i];
}
}
for(int i = 0; i <= n; i++) Rank[sa[i]] = i;
int h = 0;
height[0] = 0;
for(int i = 0; i < n; i++){
int j = sa[Rank[i] - 1];
if(h > 0) h--;
for(;j + h < n && i + h < n; h++){
if(s[j + h] != s[i + h]) break;
}
height[Rank[i] - 1] = h;
}
for(int i = 1; i <= n; i++) MIN[i][0] = height[i];
for(int j = 1; (1 << j) <= n; j++){
for(int i = 1; i <= n; i++){
MIN[i][j] = min(MIN[i][j - 1], MIN[i + (1 << (j - 1))][j - 1]);
}
}
}
int LCP(int L, int R){
L--; R--;
int k = 0;
while((1 << (k + 1)) <= R - L + 1) k++;
return min(MIN[L][k], MIN[R - (1 << k) + 1][k]);
}
char SS[N + 10];
const int MAXN = 2e5 + 11;
int root[MAXN<<5],lson[MAXN<<5],rson[MAXN<<5],sum[MAXN<<5],sz;
void Build(int& rt,int le,int ri){
rt=++sz; sum[rt]=0;
if(le==ri) return ;
int mid=(le+ri)>>1;
Build(lson[rt],le,mid);
Build(rson[rt],mid+1,ri);
}
void Update(int pre,int &rt,int le,int ri,int val){
rt=++sz;
lson[rt]=lson[pre],rson[rt]=rson[pre],sum[rt]=sum[pre]+1;
if(le==ri) return ;
int mid=(le+ri)>>1;
if(val<=mid) Update(lson[pre],lson[rt],le,mid,val);
else Update(rson[pre],rson[rt],mid+1,ri,val);
}
int Query(int st,int ed,int le,int ri,int k){
if(le==ri) return le;
int mid=(le+ri)>>1;
int t=sum[lson[ed]]-sum[lson[st]] ;
if(k<=t) Query(lson[st],lson[ed],le,mid,k);
else Query(rson[st],rson[ed],mid+1,ri,k-t);
}
int main(){
int T; scanf("%d", &T);
while(T--){
int q; scanf("%d%d", &n, &q);
scanf("%s", SS);
SA((string) SS);
sz = 0;
Build(root[0], 1, n);
for(int i = 1; i <= n; i++){
Update(root[i - 1], root[i], 1, n, sa[i] + 1);
}
while(q--){
int l, r;
scanf("%d%d", &l, &r);
int x;
if(l > r) swap(l, r );
int P = Rank[l - 1];
int L = 2, R = P, L1 = P + 1, R1 = P;
while (L <= R)
{
int M = L + R >> 1;
if (LCP(M, P) < r - l + 1)
L = M + 1;
else
R = M - 1, L1 = M;
}
L = P + 1, R = n;
while (L <= R)
{
int M = L + R >> 1;
if (LCP(P + 1, M) < r - l + 1)
R = M - 1;
else
L = M + 1, R1 = M;
}
--L1;
if(R1 - L1 + 1 < 2) puts("-1");
else
printf("%d\n", Query(root[L1 - 1], root[R1], 1, n, 2));
}
}
return 0;
}