Address
Solution
考虑在每个人的姓和名之间插入一个无关的字符。
这样问题就转化成了一些主串和一些模式串,询问每个模式串能匹配到多少个主串,以及每个主串能匹配到多少个模式串。
把所有的主串和所有的模式串用无关字符连接起来构成一个串
S
S
S ,并对
S
S
S 串求后缀数组。
(注:上面用到的所有无关字符必须两两不同,否则会出锅)
先考虑一个弱化:判断一个模式串是否是一个主串的子串。
找到
S
S
S 中这个主串对应的下标集合
S
A
S_A
SA ,以及模式串的开头在
S
S
S 中的对应下标位置
x
x
x ,
那么模式串是主串的子串,当且仅当存在
y
∈
S
A
y\in S_A
y∈SA ,
使得
L
C
P
(
S
[
x
.
.
.
∣
S
∣
]
,
S
[
y
.
.
.
∣
S
∣
]
)
≥
L
LCP(S[x...|S|],S[y...|S|])\ge L
LCP(S[x...∣S∣],S[y...∣S∣])≥L 。其中
L
L
L 为模式串的长度。
众所周知,
h
e
i
g
h
t
height
height 数组可以将
L
C
P
LCP
LCP 转成区间最小值。
所以我们可以利用树状数组等,求出
[
l
,
r
]
[l,r]
[l,r] 表示满足
h
e
i
g
h
t
height
height 的区间
[
l
+
1
,
x
]
[l+1,x]
[l+1,x] 和 区间
[
x
+
1
,
r
]
[x+1,r]
[x+1,r] 的最小值都不小于
L
L
L ,并且
l
l
l 最小,
r
r
r 最大。
那么再次转化:判断是否存在
i
∈
[
l
,
r
]
i\in[l,r]
i∈[l,r] 满足
s
a
[
i
]
∈
S
A
sa[i]\in S_A
sa[i]∈SA。
给
S
S
S 的每个下标一个
c
o
l
[
i
]
col[i]
col[i] :如果
S
[
i
]
S[i]
S[i] 来自第
k
k
k 个主串则
c
o
l
[
i
]
=
k
col[i]=k
col[i]=k ,否则为
0
0
0 。
于是,判断一个模式串是否能匹配主串
k
k
k ,就转化成是否存在
i
∈
[
l
,
r
]
i\in[l,r]
i∈[l,r] 满足
c
o
l
[
s
a
[
i
]
]
=
k
col[sa[i]]=k
col[sa[i]]=k 。
这样,求一个模式串能匹配多少个主串,就相当于统计一个区间内出现了多少种不同的数。
这是莫队的经典应用。
对于第二问,我们可以在莫队移动指针的过程中,维护每个数
x
x
x 最近一次进入区间的时间
t
x
t_x
tx ,然后如果在
r
r
r 时刻
x
x
x 移出了区间,则为
x
x
x 的答案贡献
r
−
t
x
r-t_x
r−tx 。
最后区间内还有一些数,需要为其中的每个数
x
x
x 的答案贡献
m
−
t
x
+
1
m-t_x+1
m−tx+1 。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define Pow(k, n) for (k = 1; k < n; k <<= 1, std::swap(x, y))
#define Bitr(x, n) for (; x <= n; x += x & -x)
#define Bitl(x) for (; x; x -= x & -x)
inline int read()
{
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
template <class T>
T Max(T a, T b) {return a > b ? a : b;}
const int N = 5e5 + 5;
int n, m, T, s[N], w[N], sa[N], rank[N], height[N], id[N],
l1, l2, pos[N], len[N], ans[N], lst[N], sna[N], A[N], S,
cnt[N], now_ans;
void change(int x, int v)
{
Bitr(x, T) A[x] = Max(A[x], v);
}
int ask(int x)
{
int res = 0;
Bitl(x) res = Max(res, A[x]);
return res;
}
struct query
{
int l, r, bl, id;
} que[N];
bool comp(query a, query b)
{
return a.bl < b.bl || (a.bl == b.bl && a.r < b.r);
}
void build_sa(int n, int q)
{
int i, k, *x = rank, *y = height, m = 0;
For (i, 1, n) w[m = Max(m, s[i]), x[i] = s[i]]++;
For (i, 2, m) w[i] += w[i - 1];
For (i, 1, n) sa[w[x[i]]--] = i;
Pow(k, n)
{
int tt = 0;
For (i, n - k + 1, n) y[++tt] = i;
For (i, 1, n) if (sa[i] > k) y[++tt] = sa[i] - k;
For (i, 1, m) w[i] = 0;
For (i, 1, n) w[x[i]]++;
For (i, 2, m) w[i] += w[i - 1];
Rof (i, n, 1) sa[w[x[y[i]]]--] = y[i];
m = 0;
For (i, 1, n)
{
int u = sa[i], v = sa[i - 1];
y[u] = x[u] != x[v] || x[u + k] != x[v + k] ? ++m : m;
}
if (m == n) break;
}
if (y != rank) std::copy(y, y + n + 1, rank);
k = 0;
For (i, 1, n)
{
if (k) k--;
while (s[i + k] == s[sa[rank[i] - 1] + k]) k++;
height[rank[i]] = k;
}
For (i, 2, n)
{
change(height[i] + 1, i);
if (pos[sa[i]])
{
int x = ask(len[pos[sa[i]]]);
que[pos[sa[i]]].l = x ? x : 1;
}
}
memset(A, 0, sizeof(A));
Rof (i, n, 2)
{
if (pos[sa[i]])
{
int x = ask(len[pos[sa[i]]]);
que[pos[sa[i]]].r = x ? n - x : n;
}
change(height[i] + 1, n - i + 1);
}
S = sqrt(n);
For (i, 1, q) que[i].id = i, que[i].bl = (que[i].l - 1) / S + 1;
std::sort(que + 1, que + q + 1, comp);
}
int main()
{
int i, j;
n = read(); m = read();
For (i, 1, n)
{
l1 = read();
For (j, 1, l1) s[++T] = read() + 1, id[T] = i;
s[++T] = 10001 + (i << 1) - 1;
l2 = read();
For (j, 1, l2) s[++T] = read() + 1, id[T] = i;
s[++T] = 10001 + (i << 1);
}
For (i, 1, m)
{
len[i] = read();
pos[T + 1] = i;
For (j, 1, len[i]) s[++T] = read() + 1;
s[++T] = 10001 + (n << 1) + i;
}
build_sa(T, m);
int l = 1, r = 0;
For (i, 1, m)
{
int tl = que[i].l, tr = que[i].r;
while (r < tr) if (!(cnt[id[sa[++r]]]++))
now_ans++, lst[id[sa[r]]] = i;
while (l > tl) if (!(cnt[id[sa[--l]]]++))
now_ans++, lst[id[sa[l]]] = i;
while (r > tr) if (!(--cnt[id[sa[r--]]]))
now_ans--, sna[id[sa[r + 1]]] += i - lst[id[sa[r + 1]]];
while (l < tl) if (!(--cnt[id[sa[l++]]]))
now_ans--, sna[id[sa[l - 1]]] += i - lst[id[sa[l - 1]]];
ans[que[i].id] = now_ans - (cnt[0] != 0);
}
For (i, 1, m) printf("%d\n", ans[i]);
For (i, 1, n) if (cnt[i]) sna[i] += m - lst[i] + 1;
For (i, 1, n) printf("%d ", sna[i]);
std::cout << std::endl;
return 0;
}