a180285幸运地被选做了地球到喵星球的留学生。他发现喵星人在上课前的点名现象非常有趣。 假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成。喵星球上的老师会选择M个串来点名,每次读出一个串的时候,如果这个串是一个喵星人的姓或名的子串,那么这个喵星人就必须答到。 然而,由于喵星人的字码过于古怪,以至于不能用ASCII码来表示。为了方便描述,a180285决定用数串来表示喵星人的名字。
现在你能帮助a180285统计每次点名的时候有多少喵星人答到,以及M次点名结束后每个喵星人答到多少次吗?
现在定义喵星球上的字符串给定方法:
先给出一个正整数L,表示字符串的长度,接下来L个整数表示字符串的每个字符。 输入的第一行是两个整数N和M。
接下来有N行,每行包含第i 个喵星人的姓和名两个串。姓和名都是标准的喵星球上的 字符串。
接下来有M行,每行包含一个喵星球上的字符串,表示老师点名的串。
对于每个老师点名的串输出有多少个喵星人应该答到,然后在最后一行输出每个喵星人被点到多少次。
本来是个AC自动机裸题(?),但是由于字符集很大,所以AC自动机要套个map,比较慢(不过好像可以过)。
于是就作死地写了个后缀数组。把所有字符串丢进一个母串跑后缀数组,用10001隔开。对于每个模式串(即老师点名的串),我们可以二分出它在后缀数组中的匹配区间(即与某个排名区间内的后缀的LCP大于等于它的自身串长)。
接下来考虑第一问,这相当于查询每个模式串的匹配区间内有多少个喵星人,他们姓名的某一个后缀在该区间内出现过。莫队一发即可。
对于第二问,我们不妨对于每一个姓名的后缀单独计算其贡献,但是需要减掉重复的部分。我们可以将每个匹配区间的左右端点排序,然后按排名扫一遍后缀数组,同时对于每一个姓名记录其后缀上一次出现的位置。若遇到一个左端点,以其左端点为关键字加入树状数组;碰到一个右端点则将开始加入的左端点删掉。若该位置是一个姓名的后缀,只需将这个姓名的答案加上当前在树状数组中的左端点个数,再减去上一次已经算了的部分。
设为$n$总串长,则时间复杂度为$O(n(logn + \sqrt{n}))$。实际比暴力还慢。。。
惊奇地发现这是我至今写过的最长的数据结构题?
UPD: 不久之后就不是了…
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define For(i, j, k) for(int i = j; i <= k; i++)
#define Forr(i, j, k) for(int i = j; i >= k; i--)
#define getchar getchar_unlocked
using namespace std;
const int N = 300010;
const int MaxC = 10001;
int Read(){
char c = getchar();
while(c > '9' || c < '0') c = getchar();
int x = 0;
while(c <= '9' && c >= '0') x = x * 10 + c - '0', c = getchar();
return x;
}
int sa[N], c[N], t1[N], t2[N], S[N], n;
void buildsa(int m){
int *x = t1, *y = t2;
For(i, 1, n) c[x[i] = S[i]]++;
For(i, 1, m) c[i] += c[i - 1];
Forr(i, n, 1) sa[c[x[i]]--] = i;
for(int k = 1; k < n; k <<= 1){
int p = 0;
For(i, n - k + 1, n) y[++p] = i;
For(i, 1, n) if(sa[i] > k) y[++p] = sa[i] - k;
For(i, 0, m) c[i] = 0;
For(i, 1, n) c[x[y[i]]]++;
For(i, 1, m) c[i] += c[i - 1];
Forr(i, n, 1) sa[c[x[y[i]]]--] = y[i];
swap(x, y);
x[sa[1]] = p = 1;
For(i, 2, n) x[sa[i]] = y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? p : ++p;
if(p == n) break;
m = p;
}
}
int h[N][20], rk[N], Log2[N];
void buildheight(){
For(i, 1, n) rk[sa[i]] = i;
int j = 0;
For(pos, 1, n){
if(j) --j;
int i = rk[pos];
while(sa[i] + j <= n && sa[i + 1] + j <= n && S[sa[i] + j] == S[sa[i + 1] + j]) ++j;
h[i][0] = j;
}
For(k, 1, 18)
for(int i = 1; i + (1 << k) - 1 <= n; i++)
h[i][k] = min(h[i][k - 1], h[i + (1 << (k - 1))][k - 1]);
For(i, 2, n) Log2[i] = Log2[i >> 1] + 1;
}
int query(int x, int y){
if(x == y) return n;
int t = Log2[y - x];
return min(h[x][t], h[y - (1 << t)][t]);
}
int id[N];
int AnsS[N], Sn;
int bgn[N], len[N], lft[N], rt[N], AnsP[N], Pn;
void calc_interval(){
For(i, 1, Pn){
int x = rk[bgn[i]];
int L = 1, R = x;
while(L < R){
int M = (L + R) >> 1;
if(query(M, x) >= len[i]) R = M;
else L = M + 1;
}
lft[i] = L;
L = x, R = n;
while(L < R){
int M = (L + R + 1) >> 1;
if(query(x, M) >= len[i]) L = M;
else R = M - 1;
}
rt[i] = L;
}
}
namespace questionA{
int sz, Ans[N];
struct Op{
int l, r, id;
bool operator < (const Op& B) const{
return l / sz < B.l / sz || (l / sz == B.l / sz && r < B.r);
}
}A[N];
int cnt[N], ret;
inline void add(int x){
if(x){
if(!cnt[x]) ++ret;
cnt[x]++;
}
}
inline void del(int x){
if(x){
cnt[x]--;
if(!cnt[x]) --ret;
}
}
void main(){
For(i, 1, Pn) A[i] = (Op){lft[i], rt[i], i};
sz = ceil(sqrt(n));
sort(A + 1, A + Pn + 1);
int l = A[1].l, r = A[1].r;
For(i, l, r) add(id[sa[i]]);
Ans[A[1].id] = ret;
For(i, 2, Pn){
while(l < A[i].l) del(id[sa[l]]), ++l;
while(l > A[i].l) --l, add(id[sa[l]]);
while(r < A[i].r) ++r, add(id[sa[r]]);
while(r > A[i].r) del(id[sa[r]]), --r;
Ans[A[i].id] = ret;
}
For(i, 1, Pn) printf("%d\n", Ans[i]);
}
};
namespace questionB{
int c[N];
inline int lowbit(int x){
return x & (-x);
}
void add(int x, int v){
while(x < n){
c[x] += v;
x += lowbit(x);
}
}
int sum(int x){
int ret = 0;
while(x){
ret += c[x];
x -= lowbit(x);
}
return ret;
}
int Next[N], Ans[N];
struct Op{
int l, pos, v;
bool operator < (const Op& B) const{
return pos < B.pos;
}
}A[N];
void main(){
int tot = 0, p = 1;
For(i, 1, Pn){
A[++tot] = (Op){lft[i], lft[i], 1};
A[++tot] = (Op){lft[i], rt[i] + 1, -1};
}
sort(A + 1, A + tot + 1);
For(i, 1, n){
while(p <= tot && A[p].pos == i) add(A[p].l, A[p].v), ++p;
if(id[sa[i]]){
int &x = Next[id[sa[i]]];
Ans[id[sa[i]]] += sum(i) - sum(x);
x = i;
}
}
For(i, 1, Sn) printf("%d%c", Ans[i], i == Sn ? '\n' : ' ');
}
};
int main(){
Sn = Read(), Pn = Read();
For(i, 1, Sn)
For(j, 0, 1){
int l = Read();
while(l--) S[++n] = Read(), id[n] = i;
S[++n] = MaxC;
}
For(i, 1, Pn){
int l = Read();
bgn[i] = n + 1, len[i] = l;
while(l--) S[++n] = Read();
S[++n] = MaxC;
}
buildsa(MaxC);
buildheight();
calc_interval();
questionA::main();
questionB::main();
return 0;
}