题目大意:
给你一个S串,多次询问第k大的不重复子串。
解题思路:
因为之前做过一个重复子串的第k大,所以这个题做的比较轻松。毕竟我感觉那个题目比这个难得多。
首先用一个数组直接储存1 到 i这个位置所有的不重复子串和。
查询第k大的时候直接先二分找到我们所要找的子串的起始下标。知道起始下标之后相当于我们就知道了这个串。
这个时候因为题目有要求串相同的时候保证l r尽可能的小,这里我认为应该需要一些数据结构,毕竟暴力扫的话感觉很耗时诶= =
我用的暴力写的, 即从pos这个位置向前遍历和向后遍历。height始终大于当前查找串的话就不断更新 l 的最小值,最后输出答案即可。
刚开始long long 写为了int 结果T了,还以为不能暴力,结果换了long long之后过了。。。
额,反正先写暴力写法吧= =
Ac代码:
#include<bits/stdc++.h>
#define rank ra
using namespace std;
const int maxn=3e5+10;
const int INF=1e9+7;
typedef long long ll;
char s[maxn];
int n,m,sa[maxn],rank[maxn],height[maxn],pos[maxn];
int t1[maxn],t2[maxn],r[maxn],c[maxn];
ll sum[maxn]; //储存不重复子串前缀和
bool cmp(int *r,int a,int b,int l)
{
return r[a]==r[b] && r[a+l]==r[b+l];
}
void da(int str[],int sa[],int rank[],int height[],int n,int m)
{
n++;
int i,j,p,*x=t1,*y=t2;
for(int i=0;i<m;i++) c[i]=0;
for(int i=0;i<n;i++) c[x[i]=str[i]]++;
for(int i=1;i<m;i++) c[i]+=c[i-1];
for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
for(int j=1;j<=n;j<<=1)
{
p=0;
for(int i=n-j;i<n;i++) y[p++]=i;
for(int i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
for(int i=0;i<m;i++) c[i]=0;
for(int i=0;i<n;i++) c[x[y[i]]]++;
for(int i=1;i<m;i++) c[i]+=c[i-1];
for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1,x[sa[0]]=0;
for(int i=1;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
if(p>=n) break;
m=p;
}
int k=0;
n--;
for(int i=0;i<=n;i++) rank[sa[i]]=i;
for(int i=0;i<n;i++)
{
if(k) k--;
j=sa[rank[i]-1];
while(str[i+k]==str[j+k]) k++;
height[rank[i]]=k;
}
}
int main()
{
while(scanf(" %s",s)!=EOF)
{
int ls=strlen(s),len=0;
for(int i=0;i<ls;i++) r[len++]=s[i];
r[len]=0;n=len;
da(r,sa,rank,height,n,128);
for(int i=1;i<=n;i++) //计算前缀和
sum[i]=n-sa[i]-height[i]+sum[i-1];
scanf("%d",&m);
int l=0,r=0;
ll q=0;
while(m--)
{
scanf("%lld",&q);
q=(l^r^q)+1;
int pos=lower_bound(sum+1,sum+1+n,q)-sum; //二分找到位置
if(sum[n]<q)
{
l=0,r=0;
printf("0 0\n");
continue;
}
l=sa[pos]; //处理出l和长度
q-=sum[pos-1];
int gk=height[pos]+q;
for(int i=pos;i>=1;i--) //向前向后找最小
{
if(height[i]>=gk)
l=min(l,sa[i]);
else break;
}
for(int i=pos;i<n;i++)
{
if(height[i+1]>=gk)
l=min(l,sa[i+1]);
else break;
}
r=l+gk-1;
l++,r++; //输出答案
printf("%d %d\n",l,r);
}
}
return 0;
}