(后缀数组+树状数组)
题意:给定一个由英文字母构成的字符串
s(s<105)
和
q(q<105)
个询问,每个询问包含一个区间
li,ri
和另一个字符串
zi
(
∑|zi|<105
),求
s[li,ri]
里出现了
zi
的次数?
(感觉这题出得很好啊,难点在于这个区间的限定条件)
思路:
考虑使用后缀数组,这样对于所有查询串,能用
nlog(n)
的时间复杂度处理出每个查询串在原串出现的位置的上下界
sa[l]
~
sa[r]
。然后问题就转化为对于一个查询,求出这个上下界中有哪些后缀是在
[li,ri−len(zi)+1]
范围里的(即求
sa[l]
~
sa[r]
里面有几个数字是在
[li,ri−len(zi)+1]
范围)。
接下来就可使用离线+树状数组处理。把每一个查询离线处理,按照
mx
值从小到大排序;同时将
sa[]
排序。然后处理
mx
询问时,依次将小于等于
mx
的
sa[]
的数字从小到大插入树状数组中,再查询即可。
总时间复杂度:
O(nlog(n))
代码:
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 100010;
struct Num {
int num, pos;
Num(){}
Num(int _num, int _pos):num(_num),pos(_pos){}
bool operator < (const Num &A) const {
return num < A.num;
}
};
struct Query {
int id, l, r, mx;
Query(){}
Query(int _id, int _l, int _r, int _mx):id(_id),l(_l),r(_r),mx(_mx){}
bool operator < (const Query &A)const {
return mx < A.mx;
}
};
struct SuffixArray {
int s[maxn]; // 原始字符数组(最后一个字符应必须是0,而前面的字符必须非0)
int sa[maxn]; // 后缀数组
int Rank[maxn]; // 名次数组. Rank[0]一定是n-1,即最后一个字符
int height[maxn]; // height数组
int t[maxn], t2[maxn], c[maxn]; // 辅助数组
int n; // 字符个数
void Clear() { n = 0; memset(sa, 0, sizeof(sa)); }
// m为最大字符值加1。调用之前需设置好s和n
void build_sa(int m) {
int i, *x = t, *y = t2;
for(i = 0; i < m; i++) c[i] = 0;
for(i = 0; i < n; i++) c[x[i] = s[i]]++;
for(i = 1; i < m; i++) c[i] += c[i-1];
for(i = n-1; i >= 0; i--) sa[--c[x[i]]] = i;
for(int k = 1; k <= n; k <<= 1) {
int p = 0;
for(i = n-k; i < n; i++) y[p++] = i;
for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i]-k;
for(i = 0; i < m; i++) c[i] = 0;
for(i = 0; i < n; i++) c[x[y[i]]]++;
for(i = 0; i < m; i++) c[i] += c[i-1];
for(i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
swap(x, y);
p = 1; x[sa[0]] = 0;
for(i = 1; i < n; i++)
x[sa[i]] = y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k] ? p-1 : p++;
if(p >= n) break;
m = p;
}
}
void build_height() {
int i, k = 0;
for(i = 0; i < n; i++) Rank[sa[i]] = i;
for(i = 0; i < n; i++) {
if(k) k--;
if(Rank[i] == 0) { height[Rank[i]] = 0; continue ; }
int j = sa[Rank[i]-1];
while(s[i+k] == s[j+k]) k++;
height[Rank[i]] = k;
}
}
};
SuffixArray sa;
Num mynum[maxn];
Query qu[maxn<<1];
char s[maxn], z[maxn];
int ans[maxn<<1], tree[maxn];
int lowbit(int x) {
return x&(-x);
}
void update(int pos, int num) {
for(int i=pos; i<maxn; i+=lowbit(i))
tree[i] += num;
}
int get_sum(int pos) {
int ret = 0;
for(int i=pos; i>0; i-=lowbit(i))
ret += tree[i];
return ret;
}
int cmp_suffix(char *pat, int p, int m) {
return strncmp(pat, s + sa.sa[p], m);
}
void get_query(int id, int li, int ri, char *P) {
int m = strlen(P);
qu[2*id-1] = Query(2*id-1, -1, -1, -1);
qu[2*id] = Query(2*id, -1, -1, -1);
if (cmp_suffix(P, 0, m) < 0 || cmp_suffix(P, sa.n - 1, m) > 0)
return ;
int lb = 0, rb = 0;
// get lower bound
int l = 0, r = sa.n - 1;
while(l < r) {
int mid = (l+r)/2;
if(cmp_suffix(P, mid, m) <= 0) r = mid;
else l = mid + 1;
}
lb = l;
// get upper bound
l = 0, r = sa.n - 1;
while (l < r) {
int mid = (l+r+1)/2;
if(cmp_suffix(P, mid, m) >= 0) l = mid;
else r = mid - 1;
}
rb = l;
//printf("lb: %d rb : %d mx: %d %d\n",lb,rb,li,ri-m+1);
if(rb - lb + 1 > 0) {
qu[2*id-1] = Query(2*id-1, lb, rb, li-1);
qu[2*id] = Query(2*id, lb, rb, ri-m+1);
}
return ;
}
int main() {
while(scanf("%s",s) == 1) {
int len = strlen(s);
sa.Clear();
for(int i=0; i<len; i++)
sa.s[sa.n++] = s[i] - 'a' + 1;
sa.s[sa.n++] = 0;
sa.build_sa('z' - 'a' + 2);
sa.build_height();
for(int i=1; i<sa.n; i++) // get number, 1 ~ sa.n-1
mynum[i] = Num(sa.sa[i]+1, i);
sort(mynum+1, mynum+sa.n);
int q;
scanf("%d",&q);
for(int i=1; i<=q; i++) { // get queries
int li, ri;
scanf("%d%d%s",&li,&ri,z);
get_query(i, li+1, ri+1, z);
//printf("ans : %d\n",ans);
}
sort(qu+1, qu+2*q+1);
memset(tree, 0, sizeof(tree)); // initialize BIT
int cur = 1; // solve
for(int i=1; i<=2*q; i++) {
while(cur < sa.n && mynum[cur].num <= qu[i].mx) {
update(mynum[cur].pos, 1);
cur ++;
}
if(qu[i].mx == -1)
ans[qu[i].id] = 0;
else {
int tot = get_sum(qu[i].r) - get_sum(qu[i].l-1);
ans[qu[i].id] = tot;
}
}
for(int i=1; i<=q; i++)
printf("%d\n",ans[2*i]-ans[2*i-1]);
}
return 0;
}