0%

[NOI2018]你的名字

题面描述

求一个字符串的连续非空子串与询问串本质不同的非公共子串个数

题解

一开始没看到本质不同,第二个下发数据完全不一致才发现

如果我们要求一个字符串的本质不同子串个数,那么我们只需要对于 \(Parent\) 树上的每一个节点 \(x\) 累计求和 \(maxlen(x)-maxlen(parent(x))\)

现在,又多了一个字符串,导致每个节点产生和原来不一致的贡献,考虑每个节点表示的子串是相应 \(endpos\) 前缀的后缀,也就是说对于每个位置,我们处理下以该位置结尾的前缀与另一个字符串某一子串 \(LCS\) 的最大值 \(L\),处理一下 \((0,\ L]\)\((maxlen(parent(x)), maxlen(x)]\) 的区间交算下贡献即可,\(L\) 可以通过后缀自动机上的匹配解决 之后题目又加上区间的限制,那么我们匹配到一个节点 \(x\) 后需要不停判断该节点是否符合条件,二分 \(L\),找到 \(L\) 对应的 \(x\) 的祖先,判断在 \([l+L-1,\ r]\) 有没有 \(endpos\) 即可

因为倍增顺序写反调了一下午的蒟蒻的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long int64;
inline int read(int f = 1, int x = 0, char ch = ' ')
{
while(!isdigit(ch = getchar())) if(ch == '-') f = -1;
while(isdigit(ch)) x = x*10+ch-'0', ch = getchar();
return f*x;
}
const int N = 1e6+5;
int n, q;
int root[N], val[N<<6], tot, ls[N<<6], rs[N<<6];
void add(int &x, int l, int r, int pos)
{
if(!x) x = ++tot; val[x] |= 1;
if(l == r) return;
int mid = (l+r)>>1;
if(pos <= mid) return add(ls[x], l, mid, pos);
else return add(rs[x], mid+1, r, pos);
}
bool query(int x, int l, int r, int ql, int qr)
{
if(ql > qr) return false;
if(!x||(ql <= l&&r <= qr)) return val[x];
int mid = (l+r)>>1;
if(ql <= mid&&query(ls[x], l, mid, ql, qr)) return true;
if(qr > mid&&query(rs[x], mid+1, r, ql, qr)) return true;
return false;
}
int merge(int x, int y)
{
if(!x||!y) return x|y;
int z = ++tot;
val[z] = val[x]|val[y];
ls[z] = merge(ls[x], ls[y]);
rs[z] = merge(rs[x], rs[y]);
return z;
}
char s[N];
struct Sam
{
int tot = 1, last = 1, ch[N][26], fa[N], len[N], ac[20][N], w[N];
int64 ans;
int tax[N], p[N];
void clear()
{
for(int i = 1; i <= tot; ++i)
memset(ch[i], 0, sizeof(ch[i])),
fa[i] = w[i] = tax[i] = 0;
tot = 1, last = 1, ans = 0;
}
void insert(int c, int val = -1)
{
int cur = ++tot, pre = last; last = cur;
len[cur] = len[pre]+1;
if(val == -1) add(root[cur], 1, n, len[cur]); else w[cur] = val;
while(pre&&!ch[pre][c]) ch[pre][c] = cur, pre = fa[pre];
if(!pre) return void(fa[cur] = 1);
int x = ch[pre][c];
if(len[pre]+1 == len[x]) return void(fa[cur] = x);
int y = ++tot; len[y] = len[pre]+1; fa[y] = fa[x]; fa[x] = fa[cur] = y;
memcpy(ch[y], ch[x], sizeof(ch[x]));
while(pre&&ch[pre][c] == x) ch[pre][c] = y, pre = fa[pre];
}
void build(int type = 1)
{
for(int i = 1; i <= tot; ++i) ++tax[len[i]];
for(int i = 1; i <= tot; ++i) tax[i] += tax[i-1];
for(int i = 1; i <= tot; ++i) p[tax[len[i]]--] = i;
for(int i = tot; i > 1; --i)
{
int x = p[i];
if(type) root[fa[x]] = merge(root[fa[x]], root[x]);
else
{
w[fa[x]] = max(w[x], w[fa[x]]);
ans += max(0, len[x]-max(w[x], len[fa[x]]));
}
}
if(type)
{
for(int i = 1; i <= tot; ++i)
{
int x = p[i]; ac[0][x] = fa[x];
for(int k = 1; k < 20; ++k) ac[k][x] = ac[k-1][ac[k-1][x]];
}
}
}
int find(int x, int d)
{
for(int i = 19; ~i; --i) if(len[ac[i][x]] >= d) x = ac[i][x];
return x;
}
int get(int x, int ql, int qr, int lcs)
{
if(query(root[x], 1, n, ql+lcs-1, qr)) return lcs;
int ret = 0, l = 1, r = lcs-1;
while(l <= r)
{
int mid = (l+r)>>1;
if(query(root[find(x, mid)], 1, n, ql+mid-1, qr)) ret = mid, l = mid+1;
else r = mid-1;
}
return ret;
}
}sam, buf;
int main()
{
scanf("%s", s); q = read(); n = strlen(s);
for(int i = 0; s[i]; ++i) sam.insert(s[i]-'a'); sam.build();
while(q--)
{
scanf("%s", s); int l = read(), r = read();
int x = 1, lcs = 0; buf.clear();
for(int i = 0; s[i]; ++i)
{
while(x&&!sam.ch[x][s[i]-'a']) x = sam.fa[x], lcs = sam.len[x];
if(!x) x = 1, lcs = 0;
else x = sam.ch[x][s[i]-'a'], ++lcs;
buf.insert(s[i]-'a', sam.get(x, l, r, lcs));
}
buf.build(0);
printf("%lld\n", buf.ans);
}
return 0;
}