Description
现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串(注意包括本身)。
Sample Input
3 1
abc
a
ab
Sample Output
6 1 3
考虑后缀数组来做。
把这些串串起来做一遍后缀数组。
对于height数组,我们求一个st表,用于求LCP。
然后对于每一个后缀i,我们求出一个L[i],L[i]表示[L[i],i]的区间中有k个属于不同字符串的串,并且L[i]尽量大。(这里的区间指的是按字典序排序后的)
然后对于每一个字符串的每一个后缀,我们求出它的价值,统计到答案中去,大致思路就是你暴力扫一遍,判断当前后缀长度为L的前缀是否存在k个以上一样的,然后对于这个判断,我们可以进行二分,求出一个ll满足[ll,x]的LCP大于等于L,求出一个rr满足的[x,rr]的LCP大于等于L,判断ll~rr中是否有k个及以上的元素,这个我们可以用刚刚预处理出的L数组进行判断。
然后我们想其实对于一个字符串的每一个后缀我们没必要全部暴力扫一遍,因为假设前面有一个长度为P的后缀他的最大长度的L包含了当前这个后缀的起始点,那么这个后缀其实也是不可能再往L不包含的长度拓展,于是直接用一个指针维护,扫一遍即可。
#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int _min(int x, int y) {return x < y ? x : y;}
typedef unsigned long long ULL;
typedef long long LL;
const ULL P = 131;
char ss[210000];
int n, m, Rank[210000], Rsort[210000], sa[210000], yy[210000], height[210000];
int a[210000], p[210000], L[210000], belong[210000], cnt[210000], f[20][210000], Log[210000];
ULL s[210000], o[210000];
void get_sa() {
memcpy(Rank, a, sizeof(Rank));
int tot = p[n];
for(int i = 1; i <= tot; i++) Rsort[Rank[i]]++;
for(int i = 1; i <= m; i++) Rsort[i] += Rsort[i - 1];
for(int i = tot; i >= 1; i--) sa[Rsort[Rank[i]]--] = i;
int ln = 1, u;
while(u < tot) {
int k = 0;
for(int i = tot - ln + 1; i <= tot; i++) yy[++k] = i;
for(int i = 1; i <= tot; i++) if(sa[i] - ln > 0) yy[++k] = sa[i] - ln;
memset(Rsort, 0, sizeof(Rsort));
for(int i = 1; i <= tot; i++) Rsort[Rank[i]]++;
for(int i = 1; i <= m; i++) Rsort[i] += Rsort[i - 1];
for(int i = tot; i >= 1; i--) sa[Rsort[Rank[yy[i]]]--] = yy[i];
for(int i = 1; i <= tot; i++) yy[i] = Rank[i];
u = 1; Rank[sa[1]] = 1;
for(int i = 2; i <= tot; i++) {
if(yy[sa[i]] != yy[sa[i - 1]] || yy[sa[i] + ln] != yy[sa[i - 1] + ln]) u++;
Rank[sa[i]] = u;
} m = u, ln *= 2;
}
}
int LCP(int l, int r) {
r--;
int hh = Log[r - l + 1];
return _min(f[hh][l], f[hh][r - (1 << hh) + 1]);
}
bool check(int x, int len) {
int ll, rr;
if(height[x - 1] < len) ll = x;
else {
int l = 1, r = x - 1, ans;
while(l <= r) {
int mid = (l + r) / 2;
if(LCP(mid, x) >= len) r = mid - 1, ans = mid;
else l = mid + 1;
} ll = ans;
}
if(height[x] < len) rr = x;
else {
int l = x + 1, r = p[n], ans;
while(l <= r) {
int mid = (l + r) / 2;
if(LCP(x, mid) >= len) l = mid + 1, ans = mid;
else r = mid - 1;
} rr = ans;
} return ll <= L[rr];
}
int main() {
int K; scanf("%d%d", &n, &K);
int rr = n;
for(int i = 1; i <= n; i++) {
scanf("%s", ss + 1);
int len = strlen(ss + 1);
p[i] = p[i - 1] + len;
for(int j = p[i - 1] + 1; j <= p[i]; j++)
a[j] = ss[j - p[i - 1]] - 'a' + 1, belong[j] = i;
p[i]++, a[p[i]] = 26 + i;
} o[0] = 1;
for(int i = 1; i <= p[n]; i++) o[i] = o[i - 1] * P, s[i] = s[i - 1] * P + a[i];
m = 26 + n; get_sa();
for(int i = 1; i < p[rr]; i++) {
int l = 1, r = _min(p[rr] - sa[i] + 1, p[rr] - sa[i + 1] + 1), ans = 0;
while(l <= r) {
int mid = (l + r) / 2;
if(s[sa[i] + mid - 1] - s[sa[i] - 1] * o[mid] == s[sa[i + 1] + mid - 1] - s[sa[i + 1] - 1] * o[mid]) l = mid + 1, ans = mid;
else r = mid - 1;
} height[i] = ans;
}
for(int i = 2; i <= p[rr]; i++) Log[i] = Log[i >> 1] + 1;
for(int i = 1; i < p[rr]; i++) f[0][i] = height[i];
for(int i = 1; (1 << i) < p[rr]; i++)
for(int j = 1; j + (1 << i - 1) <= p[rr]; j++)
f[i][j] = _min(f[i - 1][j], f[i - 1][j + (1 << i - 1)]);
int tot = 0, tp = 1;
for(int i = 1; i <= p[rr]; i++) {
if(!belong[sa[i]]) break;
cnt[belong[sa[i]]]++;
if(cnt[belong[sa[i]]] == 1) tot++;
while(tot > K || (tot == K && cnt[belong[sa[tp]]] > 1)) {
cnt[belong[sa[tp++]]]--; if(!cnt[belong[sa[tp - 1]]]) tot--;
} if(tot >= K) L[i] = tp;
else L[i] = -1;
}
for(int i = 1; i <= rr; i++) {
int k = 0; LL ans = 0;
for(int j = p[i - 1] + 1; j < p[i]; j++) {
int uu = p[i] - j;
if(k) k--;
while(k < uu && check(Rank[j], k + 1)) k++;
ans += k;
} printf("%lld ", ans);
}
return 0;
}