文章目录
- 总览:
- T1 [P6139 【模板】广义后缀自动机(广义 SAM)](https://www.luogu.com.cn/problem/P6139)
- T2 [SP8093 JZPGYZ - Sevenk Love Oimaster](https://www.luogu.com.cn/problem/SP8093)
- T3 [P6640 \[BJOI2020\] 封印](https://www.luogu.com.cn/problem/P6640)
- T4 [P3346 \[ZJOI2015\]诸神眷顾的幻想乡](https://www.luogu.com.cn/problem/P3346)
- T5 [CF427D Match & Catch](https://www.luogu.com.cn/problem/CF427D)
- T6 [CF452E Three strings](https://www.luogu.com.cn/problem/CF452E)
- T7 [CF235C Cyclical Quest](https://www.luogu.com.cn/problem/CF235C)
- T8 [P4022 \[CTSC2012\]熟悉的文章](https://www.luogu.com.cn/problem/P4022)
- T9 [P5212 SubString](https://www.luogu.com.cn/problem/P5212)
总览:
后缀自动机
广义后缀自动机
离线在 Trie 树上建立广义后缀自动机可以看作每次在线从父亲的位置开始插入一个字符
(在线做法也可以看做一个只有一条链的 Trie)
T1 P6139 【模板】广义后缀自动机(广义 SAM)
思路:
模板
a
n
s
=
∑
l
e
n
[
i
]
−
l
e
n
[
f
[
i
]
]
ans=\sum len[i]-len[f[i]]
ans=∑len[i]−len[f[i]]
代码:
#include <bits/stdc++.h>
using namespace std;
#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair
#define int long long
namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch() \
(p1_ == p2_ && \
(p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
? EOF \
: *p1_++)
inline int in() {
int s = 0, f = 1;
char x = getchar();
for (; x < '0' || x > '9'; x = getchar())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
if (_pos == (1 << 21) - 1) flush();
_buf[++_pos] = x;
}
inline void out(int x) {
char k[30];
int pos = 0;
if (!x) return pc('0');
if (x < 0) pc('-'), x = -x;
while (x) k[++pos] = (x % 10) | 48, x /= 10;
for (int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
int len = x.size();
for (int i = 0; i < len; i++) pc(x[i]);
}
} // namespace IO
using namespace IO;
const int A = 2e6 + 5;
int n;
char a[A];
struct SAM {
int ch[26];
int len, f;
} tr[A];
int las, sz;
inline void build() {
las = sz = 1;
tr[1].len = tr[1].f = 0;
for (int i = 0; i < 26; i++) tr[1].ch[i] = 0;
return;
}
inline int insert(int x) {
if (tr[las].ch[x]) {
int p = las, q = tr[p].ch[x];
if (tr[p].len + 1 == tr[q].len)
return q;
else {
int cur = ++sz;
tr[cur].len = tr[p].len + 1;
for (int i = 0; i < 26; i++) tr[cur].ch[i] = tr[q].ch[i];
while (p && tr[p].ch[x] == q) tr[p].ch[x] = cur, p = tr[p].f;
tr[cur].f = tr[q].f, tr[q].f = cur;
return cur;
}
} else {
int p = las, cur = ++sz;
tr[cur].len = tr[p].len + 1;
while (p && !tr[p].ch[x]) tr[p].ch[x] = cur, p = tr[p].f;
if (!p)
tr[cur].f = 1;
else {
int q = tr[p].ch[x];
if (tr[p].len + 1 == tr[q].len)
tr[cur].f = q;
else {
int cn = ++sz;
tr[cn].len = tr[p].len + 1;
for (int i = 0; i < 26; i++) tr[cn].ch[i] = tr[q].ch[i];
while (p && tr[p].ch[x] == q) tr[p].ch[x] = cn, p = tr[p].f;
tr[cn].f = tr[q].f, tr[cur].f = tr[q].f = cn;
}
}
return cur;
}
}
signed main() {
n = in();
build();
for (int i = 1; i <= n; i++) {
scanf("%s", a + 1);
las = 1;
int len = strlen(a + 1);
for (int j = 1; j <= len; j++) las = insert(a[j] - 'a');
}
int ans = 0;
for (int i = 2; i <= sz; i++) ans += tr[i].len - tr[tr[i].f].len;
out(ans), pc('\n');
flush();
return 0;
}
T2 SP8093 JZPGYZ - Sevenk Love Oimaster
思路:
SAM 一个节点代表的串集合是它的子树代表的所有串的子串
转化题意成后缀树的子树包含的模式串个数
直接线段树合并
代码:
#include <bits/stdc++.h>
using namespace std;
#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair
namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch() \
(p1_ == p2_ && \
(p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
? EOF \
: *p1_++)
inline int in() {
int s = 0, f = 1;
char x = getchar();
for (; x < '0' || x > '9'; x = getchar())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
if (_pos == (1 << 21) - 1) flush();
_buf[++_pos] = x;
}
inline void out(int x) {
char k[30];
int pos = 0;
if (!x) return pc('0');
if (x < 0) pc('-'), x = -x;
while (x) k[++pos] = (x % 10) | 48, x /= 10;
for (int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
int len = x.size();
for (int i = 0; i < len; i++) pc(x[i]);
}
} // namespace IO
using namespace IO;
const int A = 5e5 + 5;
const int logA = 20;
int n, Q;
char a[A];
namespace SAM {
struct Node {
int ch[26];
int len, f;
} tr[A];
int las, sz;
inline void clean() {
las = sz = 1;
tr[1].len = tr[1].f = 0;
for (int i = 0; i < 26; i++) tr[1].ch[i] = 0;
return;
}
inline int insert(int x) {
if (tr[las].ch[x]) {
int p = las, q = tr[p].ch[x];
if (tr[q].len == tr[p].len + 1)
return q;
else {
int cur = ++sz;
tr[cur].len = tr[p].len + 1;
for (int i = 0; i < 26; i++) tr[cur].ch[i] = tr[q].ch[i];
while (p && tr[p].ch[x] == q) tr[p].ch[x] = cur, p = tr[p].f;
tr[cur].f = tr[q].f, tr[q].f = cur;
return cur;
}
}
int p = las, cur = ++sz;
tr[cur].len = tr[p].len + 1;
while (p && !tr[p].ch[x]) tr[p].ch[x] = cur, p = tr[p].f;
if (!p)
tr[cur].f = 1;
else {
int q = tr[p].ch[x];
if (tr[q].len == tr[p].len + 1)
tr[cur].f = q;
else {
int cn = ++sz;
tr[cn].len = tr[p].len + 1;
for (int i = 0; i < 26; i++) tr[cn].ch[i] = tr[q].ch[i];
while (p && tr[p].ch[x] == q) tr[p].ch[x] = cn, p = tr[p].f;
tr[cn].f = tr[q].f, tr[q].f = tr[cur].f = cn;
}
}
return cur;
}
} // namespace SAM
vector<int> t[A];
int head[A], tot_road;
struct Road {
int nex, to;
} road[2 * A];
inline void edge(int x, int y) {
road[++tot_road] = {head[x], y}, head[x] = tot_road;
}
struct SGT {
int ls, rs;
int num;
} tr[A * logA];
int rt[A], tot;
#define ls(x) tr[x].ls
#define rs(x) tr[x].rs
inline void pushup(int x) { tr[x].num = tr[ls(x)].num + tr[rs(x)].num; }
inline void insert(int &x, int l, int r, int w) {
int y = x;
x = ++tot;
tr[x] = tr[y];
if (l == r) {
tr[x].num = 1;
return;
}
int mid = (l + r) >> 1;
if (w <= mid)
insert(ls(x), l, mid, w);
else
insert(rs(x), mid + 1, r, w);
pushup(x);
return;
}
inline int merge(int x, int y, int l, int r) {
if (!x || !y) return x | y;
if (l == r) {
tr[x].num = tr[x].num | tr[y].num;
return x;
}
int mid = (l + r) >> 1;
ls(x) = merge(ls(x), ls(y), l, mid), rs(x) = merge(rs(x), rs(y), mid + 1, r);
pushup(x);
return x;
}
#undef ls
#undef rs
int res[A];
inline void DFS(int x) {
for (int y = head[x]; y; y = road[y].nex) {
int z = road[y].to;
DFS(z);
rt[x] = merge(rt[x], rt[z], 1, n);
}
for (int i = 0; i < t[x].size(); i++) insert(rt[x], 1, n, t[x][i]);
res[x] = tr[rt[x]].num;
return;
}
signed main() {
n = in(), Q = in();
SAM::clean();
for (int i = 1; i <= n; i++) {
scanf("%s", a + 1);
SAM::las = 1;
int len = strlen(a + 1);
for (int j = 1; j <= len; j++) {
SAM::las = SAM::insert(a[j] - 'a');
t[SAM::las].pb(i);
}
}
for (int i = 2; i <= SAM::sz; i++) edge(SAM::tr[i].f, i);
DFS(1);
while (Q--) {
scanf("%s", a + 1);
int len = strlen(a + 1);
int p = 1, pos = 0;
for (int i = 1; i <= len; i++) {
if (!SAM::tr[p].ch[a[i] - 'a']) {
pos = 1;
break;
}
p = SAM::tr[p].ch[a[i] - 'a'];
}
if (pos)
out("0\n");
else
out(res[p]), pc('\n');
}
flush();
return 0;
}
T3 P6640 [BJOI2020] 封印
思路:
即求
t
t
t 与
s
[
l
,
l
]
−
s
[
l
,
r
]
s[l,l]-s[l,r]
s[l,l]−s[l,r] 的最长公共后缀长度
预处理
t
t
t 与
s
[
1
,
k
]
s[1,k]
s[1,k] 的最长公共后缀长度
v
a
l
i
val_i
vali
即求
m
a
x
i
=
l
r
(
m
i
n
(
i
−
l
+
1
,
v
a
l
i
)
)
max_{i=l}^r(min(i-l+1,val_i))
maxi=lr(min(i−l+1,vali))
发现
i
−
l
+
1
i-l+1
i−l+1 在
i
i
i 右移的过程中每次加一,
v
a
l
i
val_i
vali 每次加一或清零
所以
f
(
i
)
=
(
i
−
l
+
1
)
−
v
a
l
i
f(i)=(i-l+1)-val_i
f(i)=(i−l+1)−vali 单增
二分零点,零点左边取
w
−
l
+
1
w-l+1
w−l+1,右边取
m
a
x
(
v
a
l
i
)
max(val_i)
max(vali)
再用
S
T
ST
ST 表维护区间最大即可
代码:
#include <bits/stdc++.h>
using namespace std;
#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair
namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch() \
(p1_ == p2_ && \
(p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
? EOF \
: *p1_++)
inline int in() {
int s = 0, f = 1;
char x = getchar();
for (; x < '0' || x > '9'; x = getchar())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
if (_pos == (1 << 21) - 1) flush();
_buf[++_pos] = x;
}
inline void out(int x) {
char k[30];
int pos = 0;
if (!x) return pc('0');
if (x < 0) pc('-'), x = -x;
while (x) k[++pos] = (x % 10) | 48, x /= 10;
for (int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
int len = x.size();
for (int i = 0; i < len; i++) pc(x[i]);
}
} // namespace IO
using namespace IO;
const int A = 5e5 + 5;
const int logA = 21;
char ss[A], tt[A];
int lg[A], val[A], st[A][logA];
struct SAM {
int las, sz;
int ch[A][26], len[A], f[A];
inline void clean() {
las = sz = 1;
len[1] = f[1] = 0;
for (int i = 0; i < 26; i++) ch[1][i] = 0;
return;
}
inline int insert(int x) {
int p = las, cur = ++sz;
len[cur] = len[p] + 1;
while (p && !ch[p][x]) ch[p][x] = cur, p = f[p];
if (!p)
f[cur] = 1;
else {
int q = ch[p][x];
if (len[q] == len[p] + 1)
f[cur] = q;
else {
int cn = ++sz;
len[cn] = len[p] + 1;
for (int i = 0; i < 26; i++) ch[cn][i] = ch[q][i];
while (p && ch[p][x] == q) ch[p][x] = cn, p = f[p];
f[cn] = f[q], f[q] = f[cur] = cn;
}
}
return cur;
}
inline void build() {
clean();
int kk = strlen(tt + 1);
for (int i = 1; i <= kk; i++) las = insert(tt[i] - 'a');
return;
}
inline void prepare() {
int kk = strlen(ss + 1);
int p = 1, ans = 0;
for (int i = 1; i <= kk; i++) {
while (p && !ch[p][ss[i] - 'a']) p = f[p], ans = len[p];
if (!p) p = 1, ans = len[p];
if (ch[p][ss[i] - 'a']) {
p = ch[p][ss[i] - 'a'];
ans++;
}
val[i] = ans;
}
for (int i = 2; i <= kk; i++) lg[i] = lg[i >> 1] + 1;
for (int i = 1; i <= kk; i++) st[i][0] = val[i];
for (int i = 1; i <= lg[kk]; i++)
for (int j = 1; j + (1 << i) - 1 <= kk; j++)
st[j][i] = max(st[j][i - 1], st[j + (1 << (i - 1))][i - 1]);
return;
}
} S;
inline int find(int l, int r) {
int k = lg[r - l + 1];
return max(st[l][k], st[r - (1 << k) + 1][k]);
}
inline int query(int l, int r) {
int L = l, R = r, ans = l - 1;
while (L <= R) {
int mid = (L + R) >> 1;
if ((mid - l + 1) - val[mid] <= 0)
L = mid + 1, ans = mid;
else
R = mid - 1;
}
return ans < r ? max(ans - l + 1, find(ans + 1, r)) : ans - l + 1;
}
signed main() {
scanf("%s%s", ss + 1, tt + 1);
S.build();
S.prepare();
int Q = in();
while (Q--) {
int l = in(), r = in();
out(query(l, r)), pc('\n');
}
flush();
return 0;
}
T4 P3346 [ZJOI2015]诸神眷顾的幻想乡
思路:
叶子节点数小于等于十
于是以每个叶子节点为根,遍历整棵树,插入 SAM
这样的 SAM 中必定包含所有子串
最后统计答案即可
代码:
#include <bits/stdc++.h>
using namespace std;
#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair
namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch() \
(p1_ == p2_ && \
(p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
? EOF \
: *p1_++)
inline int in() {
int s = 0, f = 1;
char x = ch();
for (; x < '0' || x > '9'; x = ch())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
if (_pos == (1 << 21) - 1) flush();
_buf[++_pos] = x;
}
inline void out(LL x) {
char k[30];
int pos = 0;
if (!x) return pc('0');
if (x < 0) pc('-'), x = -x;
while (x) k[++pos] = (x % 10) | 48, x /= 10;
for (int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
int len = x.size();
for (int i = 0; i < len; i++) pc(x[i]);
}
} // namespace IO
using namespace IO;
const int A = 3e6 + 5;
int n, m;
int head[A], tot_road;
struct Road {
int nex, to;
} road[2 * A];
inline void edge(int x, int y) {
road[++tot_road] = {head[x], y}, head[x] = tot_road;
}
int rd[A], col[A];
int pt[A];
struct SAM {
int ch[20];
int len, f;
} tr[A];
int sz;
inline void clean() {
sz = 1;
tr[1].len = tr[1].f = 0;
for (int i = 0; i < m; i++) tr[1].ch[i] = 0;
return;
}
inline int insert(int x, int las) {
if (tr[las].ch[x]) {
int p = las, q = tr[las].ch[x];
if (tr[p].len + 1 == tr[q].len)
return q;
else {
int cur = ++sz;
tr[cur].len = tr[p].len + 1;
for (int i = 0; i < m; i++) tr[cur].ch[i] = tr[q].ch[i];
while (p && tr[p].ch[x] == q) tr[p].ch[x] = cur, p = tr[p].f;
tr[cur].f = tr[q].f, tr[q].f = cur;
return cur;
}
}
int p = las, cur = ++sz;
tr[cur].len = tr[p].len + 1;
while (p && !tr[p].ch[x]) tr[p].ch[x] = cur, p = tr[p].f;
if (!p)
tr[cur].f = 1;
else {
int q = tr[p].ch[x];
if (tr[p].len + 1 == tr[q].len)
tr[cur].f = q;
else {
int cn = ++sz;
tr[cn].len = tr[p].len + 1;
for (int i = 0; i < m; i++) tr[cn].ch[i] = tr[q].ch[i];
while (p && tr[p].ch[x] == q) tr[p].ch[x] = cn, p = tr[p].f;
tr[cn].f = tr[q].f, tr[q].f = tr[cur].f = cn;
}
}
return cur;
}
inline void DFS(int fa, int x) {
pt[x] = insert(col[x], pt[fa]);
for (int y = head[x]; y; y = road[y].nex) {
int z = road[y].to;
if (z == fa) continue;
DFS(x, z);
}
return;
}
signed main() {
n = in(), m = in();
for (int i = 1; i <= n; i++) col[i] = in();
for (int i = 1; i < n; i++) {
int u = in(), v = in();
edge(u, v), edge(v, u);
rd[u]++, rd[v]++;
}
clean();
pt[0] = 1;
for (int i = 1; i <= n; i++)
if (rd[i] == 1) DFS(0, i);
LL ans = 0;
for (int i = 2; i <= sz; i++) ans += tr[i].len - tr[tr[i].f].len;
out(ans), pc('\n');
flush();
return 0;
}
T5 CF427D Match & Catch
思路:
SAM 中从叶子节点向上跳,节点代表的串的出现次数每次加一
所以一个串在 SAM 中的出现次数为子树大小
建立广义后缀自动机,对每个结点分别统计在两个串中的出现次数
一个节点代表的最小值子串长度为父亲的
l
e
n
+
1
len+1
len+1
用出现次数都为一的点更新答案
代码:
#include <bits/stdc++.h>
using namespace std;
#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair
namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch() \
(p1_ == p2_ && \
(p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
? EOF \
: *p1_++)
inline int in() {
int s = 0, f = 1;
char x = ch();
for (; x < '0' || x > '9'; x = ch())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
if (_pos == (1 << 21) - 1) flush();
_buf[++_pos] = x;
}
inline void out(int x) {
char k[30];
int pos = 0;
if (!x) return pc('0');
if (x < 0) pc('-'), x = -x;
while (x) k[++pos] = (x % 10) | 48, x /= 10;
for (int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
int len = x.size();
for (int i = 0; i < len; i++) pc(x[i]);
}
} // namespace IO
using namespace IO;
const int A = 5e5 + 5;
const int INF = 1e9;
char a[A];
struct SAM {
int ch[26];
int len, f;
} tr[A];
int las, sz;
inline void build() {
las = sz = 1;
tr[1].len = tr[1].f = 0;
for (int i = 0; i < 26; i++) tr[1].ch[i] = 0;
return;
}
inline int insert(int x) {
if (tr[las].ch[x]) {
int p = las, q = tr[p].ch[x];
if (tr[q].len == tr[p].len + 1)
return q;
else {
int cur = ++sz;
tr[cur].len = tr[p].len + 1;
for (int i = 0; i < 26; i++) tr[cur].ch[i] = tr[q].ch[i];
while (p && tr[p].ch[x] == q) tr[p].ch[x] = cur, p = tr[p].f;
tr[cur].f = tr[q].f, tr[q].f = cur;
return cur;
}
} else {
int p = las, cur = ++sz;
tr[cur].len = tr[p].len + 1;
while (p && !tr[p].ch[x]) tr[p].ch[x] = cur, p = tr[p].f;
if (!p)
tr[cur].f = 1;
else {
int q = tr[p].ch[x];
if (tr[q].len == tr[p].len + 1)
tr[cur].f = q;
else {
int cn = ++sz;
tr[cn].len = tr[p].len + 1;
for (int i = 0; i < 26; i++) tr[cn].ch[i] = tr[q].ch[i];
while (p && tr[p].ch[x] == q) tr[p].ch[x] = cn, p = tr[p].f;
tr[cn].f = tr[q].f, tr[cur].f = tr[q].f = cn;
}
}
return cur;
}
}
int num[2][A];
int t[A], tmp[A];
inline void topo() {
for (int i = 1; i <= sz; i++) tmp[tr[i].len]++;
for (int i = 1; i <= sz; i++) tmp[i] += tmp[i - 1];
for (int i = 1; i <= sz; i++) t[tmp[tr[i].len]--] = i;
return;
}
signed main() {
build();
scanf("%s", a + 1);
int len = strlen(a + 1);
for (int i = 1; i <= len; i++) las = insert(a[i] - 'a'), num[0][las] = 1;
scanf("%s", a + 1);
len = strlen(a + 1);
las = 1;
for (int i = 1; i <= len; i++) las = insert(a[i] - 'a'), num[1][las] = 1;
topo();
for (int i = sz; i; i--)
num[0][tr[t[i]].f] += num[0][t[i]], num[1][tr[t[i]].f] += num[1][t[i]];
int ans = INF;
for (int i = 2; i <= sz; i++)
if (num[0][i] == 1 && num[1][i] == 1) ans = min(ans, tr[tr[i].f].len + 1);
if (ans == INF)
out("-1\n");
else
out(ans), pc('\n');
flush();
return 0;
}
T6 CF452E Three strings
思路:
建立广义 SAM,对每个结点统计在三个串中的出现次数,每个节点对答案的贡献为出现次数的积,用每个节点区间更新答案,差分维护
代码:
#include <bits/stdc++.h>
using namespace std;
#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair
#define int long long
namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch() \
(p1_ == p2_ && \
(p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
? EOF \
: *p1_++)
inline int in() {
int s = 0, f = 1;
char x = ch();
for (; x < '0' || x > '9'; x = ch())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
if (_pos == (1 << 21) - 1) flush();
_buf[++_pos] = x;
}
inline void out(int x) {
char k[30];
int pos = 0;
if (!x) return pc('0');
if (x < 0) pc('-'), x = -x;
while (x) k[++pos] = (x % 10) | 48, x /= 10;
for (int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
int len = x.size();
for (int i = 0; i < len; i++) pc(x[i]);
}
} // namespace IO
using namespace IO;
const int A = 6e5 + 5;
const int mod = 1e9 + 7;
const int INF = 1e9;
inline int add(int x, int y) { return x + y >= mod ? x + y - mod : x + y; }
inline int dec(int x, int y) { return x - y < 0 ? x - y + mod : x - y; }
inline int mul(int x, int y) {
return x * y % mod < 0 ? x * y % mod + mod : x * y % mod;
}
inline void Add(int &x, int y) { x = add(x, y); }
inline void Dec(int &x, int y) { x = dec(x, y); }
inline void Mul(int &x, int y) { x = mul(x, y); }
char a[A];
struct SAM {
int n = INF;
int las, sz;
int ch[A][26], len[A], f[A];
inline void clean() {
las = sz = 1;
len[1] = f[1] = 0;
for (int i = 0; i < 26; i++) ch[1][i] = 0;
return;
}
inline int insert(int x) {
if (ch[las][x]) {
int p = las, q = ch[p][x];
if (len[p] + 1 == len[q])
return q;
else {
int cur = ++sz;
len[cur] = len[p] + 1;
for (int i = 0; i < 26; i++) ch[cur][i] = ch[q][i];
while (p && ch[p][x] == q) ch[p][x] = cur, p = f[p];
f[cur] = f[q], f[q] = cur;
return cur;
}
} else {
int p = las, cur = ++sz;
len[cur] = len[p] + 1;
while (p && !ch[p][x]) ch[p][x] = cur, p = f[p];
if (!p)
f[cur] = 1;
else {
int q = ch[p][x];
if (len[q] == len[p] + 1)
f[cur] = q;
else {
int cn = ++sz;
len[cn] = len[p] + 1;
for (int i = 0; i < 26; i++) ch[cn][i] = ch[q][i];
while (p && ch[p][x] == q) ch[p][x] = cn, p = f[p];
f[cn] = f[q], f[q] = f[cur] = cn;
}
}
return cur;
}
}
int num[3][A];
inline void build(int now) {
las = 1;
int len = strlen(a + 1);
n = min(n, len);
for (int i = 1; i <= len; i++) las = insert(a[i] - 'a'), num[now][las]++;
return;
}
int c[A], t[A];
inline void topo() {
for (int i = 1; i <= sz; i++) t[len[i]]++;
for (int i = 1; i <= sz; i++) t[i] += t[i - 1];
for (int i = 1; i <= sz; i++) c[t[len[i]]--] = i;
return;
}
int res[A];
inline void query() {
topo();
for (int i = sz; i; i--)
for (int j = 0; j < 3; j++) num[j][f[c[i]]] += num[j][c[i]];
for (int i = 2; i <= sz; i++)
Add(res[len[f[i]] + 1], mul(mul(num[0][i], num[1][i]), num[2][i])),
Dec(res[len[i] + 1], mul(mul(num[0][i], num[1][i]), num[2][i]));
for (int i = 1; i <= n; i++) Add(res[i], res[i - 1]), out(res[i]), pc(' ');
return;
}
} S;
signed main() {
S.clean();
for (int i = 0; i < 3; i++) {
scanf("%s", a + 1);
S.build(i);
}
S.query(), pc('\n');
flush();
return 0;
}
T7 CF235C Cyclical Quest
思路:
循环同构即每次删除首字母,然后在尾部插入一个字母
对于删除首字母可以跳 fail树,直到匹配长度小于删除后的字符串长度
建立 SAM 匹配即可
代码:
#include <bits/stdc++.h>
using namespace std;
#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair
namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch() \
(p1_ == p2_ && \
(p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
? EOF \
: *p1_++)
inline int in() {
int s = 0, f = 1;
char x = getchar();
for (; x < '0' || x > '9'; x = getchar())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
if (_pos == (1 << 21) - 1) flush();
_buf[++_pos] = x;
}
inline void out(int x) {
char k[30];
int pos = 0;
if (!x) return pc('0');
if (x < 0) pc('-'), x = -x;
while (x) k[++pos] = (x % 10) | 48, x /= 10;
for (int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
int len = x.size();
for (int i = 0; i < len; i++) pc(x[i]);
}
} // namespace IO
using namespace IO;
const int A = 2e6 + 5;
char a[A];
struct SAM {
int ch[A][26], f[A], len[A], num[A];
int sz, las;
inline int insert(int x) {
int p = las, cur = ++sz;
len[cur] = len[p] + 1;
while (p && !ch[p][x]) ch[p][x] = cur, p = f[p];
if (!p)
f[cur] = 1;
else {
int q = ch[p][x];
if (len[q] == len[p] + 1)
f[cur] = q;
else {
int cn = ++sz;
len[cn] = len[p] + 1;
for (int i = 0; i < 26; i++) ch[cn][i] = ch[q][i];
while (p && ch[p][x] == q) ch[p][x] = cn, p = f[p];
f[cn] = f[q], f[q] = f[cur] = cn;
}
}
return cur;
}
inline void clean() {
sz = las = 1;
f[1] = len[1] = 0;
for (int i = 0; i < 26; i++) ch[1][i] = 0;
return;
}
int t[A], c[A];
inline void topo() {
for (int i = 1; i <= sz; i++) t[len[i]]++;
for (int i = 1; i <= sz; i++) t[i] += t[i - 1];
for (int i = 1; i <= sz; i++) c[t[len[i]]--] = i;
return;
}
inline void build() {
clean();
int L = strlen(a + 1);
for (int i = 1; i <= L; i++) las = insert(a[i] - 'a'), num[las] = 1;
topo();
for (int i = sz; i; i--) num[f[c[i]]] += num[c[i]];
return;
}
int ex[A], st[A], top;
int ans;
inline int query() {
top = 0, ans = 0;
int L = strlen(a + 1), now = 0;
int p = 1;
for (int i = 1; i <= L; i++) {
while (p && !ch[p][a[i] - 'a']) p = f[p], now = len[p];
if (!p) p = 1, now = 0;
if (ch[p][a[i] - 'a']) p = ch[p][a[i] - 'a'], now++;
}
if (!ex[p] && now == L) {
ans += num[p];
ex[p] = 1;
st[++top] = p;
while (p && len[f[p]] + 1 >= L) p = f[p];
now--;
}
for (int i = 1; i < L; i++) {
while (p && !ch[p][a[i] - 'a']) p = f[p], now = len[p];
if (!p) p = 1, now = 0;
if (ch[p][a[i] - 'a']) p = ch[p][a[i] - 'a'], now++;
if (!ex[p] && now == L) {
ans += num[p];
ex[p] = 1;
st[++top] = p;
while (p && len[f[p]] + 1 >= L) p = f[p];
now--;
}
}
for (int i = 1; i <= top; i++) ex[st[i]] = 0;
return ans;
}
} S;
signed main() {
scanf("%s", a + 1);
S.build();
int Q = in();
while (Q--) {
scanf("%s", a + 1);
out(S.query()), pc('\n');
}
flush();
return 0;
}
T8 P4022 [CTSC2012]熟悉的文章
思路:
对于每篇作文处理
直接求
L
0
L_0
L0 不好求,于是二分答案
考虑怎么判断对于每个位置预处理出开头到这个位置的后缀最大匹配长度
有 dp方程:
f
i
=
m
a
x
(
f
i
−
1
,
f
j
+
i
−
j
)
(
j
∈
[
i
−
m
a
x
l
e
n
,
i
−
L
0
]
)
f_i=max(f_{i-1},f_j+i-j) (j\in [i-maxlen,i-L_0])
fi=max(fi−1,fj+i−j)(j∈[i−maxlen,i−L0])
m
a
x
l
e
n
maxlen
maxlen 为开头到这个位置的后缀最大匹配长度
发现
i
−
m
a
x
l
e
n
i-maxlen
i−maxlen 不降,于是可以单调队列优化成
O
(
n
)
O(n)
O(n)
代码:
#include <bits/stdc++.h>
using namespace std;
#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair
namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch() \
(p1_ == p2_ && \
(p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
? EOF \
: *p1_++)
inline int in() {
int s = 0, f = 1;
char x = getchar();
for (; x < '0' || x > '9'; x = getchar())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
if (_pos == (1 << 21) - 1) flush();
_buf[++_pos] = x;
}
inline void out(int x) {
char k[30];
int pos = 0;
if (!x) return pc('0');
if (x < 0) pc('-'), x = -x;
while (x) k[++pos] = (x % 10) | 48, x /= 10;
for (re int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
int len = x.size();
for (re int i = 0; i < len; ++i) pc(x[i]);
}
} // namespace IO
using namespace IO;
const int A = 2e6 + 5;
int n, m;
char a[A];
int mx[A], f[A];
int q[A], ql, qr;
inline int check(int now) {
int L = strlen(a + 1);
for (re int i = 1; i <= L; ++i) f[i] = 0;
ql = 1, qr = 0;
for (re int i = 1; i <= L; ++i) {
while (ql <= qr && q[ql] < i - mx[i]) ql++;
f[i] = f[i - 1];
if (ql <= qr) f[i] = max(f[i], f[q[ql]] + i - q[ql]);
if (i - now + 1 >= 0) {
while (ql <= qr && f[i - now + 1] - (i - now + 1) >= f[q[qr]] - q[qr])
qr--;
q[++qr] = i - now + 1;
}
}
if (f[L] >= (int)ceil(0.9 * L)) return 1;
return 0;
}
inline void solve() {
int L = 0, R = strlen(a + 1), ans = 0;
while (L <= R) {
int mid = (L + R) >> 1;
if (check(mid))
L = mid + 1, ans = mid;
else
R = mid - 1;
}
out(ans), pc('\n');
return;
}
struct SAM {
int ch[A][2], f[A], len[A];
int sz;
inline void rebuild() {
sz = 1;
f[1] = len[1] = 0;
for (re int i = 0; i < 2; ++i) ch[1][i] = 0;
return;
}
inline int insert(int x, int las) {
if (ch[las][x]) {
int p = las, q = ch[las][x];
if (len[p] + 1 == len[q])
return q;
else {
int cur = ++sz;
len[cur] = len[p] + 1;
for (re int i = 0; i < 2; ++i) ch[cur][i] = ch[q][i];
while (p && ch[p][x] == q) ch[p][x] = cur, p = f[p];
f[cur] = f[q], f[q] = cur;
return cur;
}
} else {
int p = las, cur = ++sz;
len[cur] = len[p] + 1;
while (p && !ch[p][x]) ch[p][x] = cur, p = f[p];
if (!p)
f[cur] = 1;
else {
int q = ch[p][x];
if (len[p] + 1 == len[q])
f[cur] = q;
else {
int cn = ++sz;
len[cn] = len[p] + 1;
for (re int i = 0; i < 2; ++i) ch[cn][i] = ch[q][i];
while (p && ch[p][x] == q) ch[p][x] = cn, p = f[p];
f[cn] = f[q], f[q] = f[cur] = cn;
}
}
return cur;
}
}
inline void add() {
int las = 1;
int L = strlen(a + 1);
for (re int i = 1; i <= L; ++i) las = insert(a[i] - '0', las);
return;
}
inline void prepare() {
int L = strlen(a + 1), p = 1, res = 0;
for (re int i = 1; i <= L; ++i) {
int x = a[i] - '0';
while (p && !ch[p][x]) p = f[p], res = len[p];
if (!p)
p = 1, res = 0;
else
p = ch[p][x], res++;
mx[i] = res;
}
return;
}
} T;
signed main() {
n = in(), m = in();
T.rebuild();
while (m--) {
scanf("%s", a + 1);
T.add();
}
while (n--) {
scanf("%s", a + 1);
T.prepare();
solve();
}
flush();
return 0;
}
T9 P5212 SubString
思路:
动态加点,求子树和
用 LCT 维护
每次链加,增加辅助节点要从复制的节点处继承信息
代码:
#include <bits/stdc++.h>
using namespace std;
#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair
namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch() \
(p1_ == p2_ && \
(p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
? EOF \
: *p1_++)
inline int in() {
int s = 0, f = 1;
char x = getchar();
for (; x < '0' || x > '9'; x = getchar())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
if (_pos == (1 << 21) - 1) flush();
_buf[++_pos] = x;
}
inline void out(int x) {
char k[30];
int pos = 0;
if (!x) return pc('0');
if (x < 0) pc('-'), x = -x;
while (x) k[++pos] = (x % 10) | 48, x /= 10;
for (re int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
int len = x.size();
for (re int i = 0; i < len; ++i) pc(x[i]);
}
} // namespace IO
using namespace IO;
string decodeWithMask(string s, int mask) {
for (int j = 0; j < s.size(); j++) {
mask = (mask * 131 + j) % s.size();
char t = s[j];
s[j] = s[mask];
s[mask] = t;
}
return s;
}
const int A = 1e7 + 5;
string a;
int mk = 0;
struct LCT {
int ch[A][2], f[A], rev[A], val[A], tag[A];
inline int isroot(int x) { return ch[f[x]][0] != x && ch[f[x]][1] != x; }
inline void reverse(int x) {
if (x) swap(ch[x][0], ch[x][1]), rev[x] ^= 1;
}
inline void pushdown(int x) {
if (rev[x]) reverse(ch[x][0]), reverse(ch[x][1]), rev[x] ^= 1;
if (tag[x]) {
if (ch[x][0]) {
val[ch[x][0]] += tag[x];
tag[ch[x][0]] += tag[x];
}
if (ch[x][1]) {
val[ch[x][1]] += tag[x];
tag[ch[x][1]] += tag[x];
}
tag[x] = 0;
}
return;
}
inline void rotate(int x) {
int y = f[x], z = f[y];
int k = (ch[y][1] == x);
if (z && !isroot(y)) ch[z][(ch[z][1] == y)] = x;
f[x] = z, ch[y][k] = ch[x][k ^ 1];
if (ch[x][k ^ 1]) f[ch[x][k ^ 1]] = y;
ch[x][k ^ 1] = y, f[y] = x;
return;
}
int st[A], top;
inline void pushpath(int x) {
top = 0;
st[++top] = x;
for (int i = x; !isroot(i); i = f[i]) st[++top] = f[i];
for (int i = top; i; i--) {
pushdown(st[i]);
}
return;
}
inline void splay(int x) {
pushpath(x);
while (!isroot(x)) {
int y = f[x], z = f[y];
if (!isroot(y)) {
if ((ch[z][1] == y) == (ch[y][1] == x))
rotate(y);
else
rotate(x);
}
rotate(x);
}
return;
}
inline void access(int x) {
for (int y = 0; x; y = x, x = f[x]) splay(x), ch[x][1] = y;
}
inline int findroot(int x) {
access(x), splay(x);
while (ch[x][0]) pushdown(x), x = ch[x][0];
return x;
}
inline void makeroot(int x) { access(x), splay(x), reverse(x); }
inline void split(int x, int y) { makeroot(x), access(y), splay(y); }
inline void link(int x, int y) {
makeroot(x);
if (findroot(y) != x) f[x] = y;
}
inline void cut(int x, int y) {
makeroot(x);
if (findroot(y) == x && f[x] == y && ch[y][0] == x) f[x] = 0, ch[y][0] = 0;
}
inline void modify(int x, int y, int w) {
split(x, y);
val[y] += w, tag[y] += w;
}
inline int query(int x) {
splay(x);
return val[x];
}
} T;
struct SAM {
int ch[A][26], len[A], f[A];
int sz, las;
inline void build() {
sz = las = 1;
len[1] = f[1] = 0;
for (int i = 0; i < 26; i++) ch[1][i] = 0;
return;
}
inline int insert(int x) {
int p = las, cur = ++sz;
len[cur] = len[p] + 1;
while (p && !ch[p][x]) ch[p][x] = cur, p = f[p];
if (!p)
f[cur] = 1, T.link(1, cur);
else {
int q = ch[p][x];
if (len[q] == len[p] + 1)
f[cur] = q, T.link(q, cur);
else {
int cn = ++sz;
len[cn] = len[p] + 1;
for (int i = 0; i < 26; i++) ch[cn][i] = ch[q][i];
while (p && ch[p][x] == q) ch[p][x] = cn, p = f[p];
if (f[q]) T.cut(q, f[q]);
f[cn] = f[q], f[q] = f[cur] = cn;
if (f[cn]) T.link(f[cn], cn);
T.link(cn, q), T.link(cn, cur);
T.val[cn] = T.val[q];
}
}
return cur;
}
inline void insert() {
int L = a.size();
for (int i = 0; i < L; i++) {
las = insert(a[i] - 'A');
T.modify(1, las, 1);
}
return;
}
inline int query() {
int p = 1, L = a.size();
for (int i = 0; i < L; i++) {
int x = a[i] - 'A';
if (!ch[p][x]) return 0;
p = ch[p][x];
}
return T.query(p);
}
} S;
signed main() {
int Q = in();
S.build();
cin >> a;
S.insert();
while (Q--) {
cin >> a;
if (a == "ADD") {
cin >> a;
a = decodeWithMask(a, mk);
S.insert();
} else {
cin >> a;
a = decodeWithMask(a, mk);
int now = S.query();
mk ^= now;
out(now), pc('\n');
}
}
flush();
return 0;
}