20200908 練習:(廣義)字尾自動機
文章目錄
- 總覽:
- 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)
總覽:
字尾自動機
廣義字尾自動機
離線在 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;
}
相關文章
- 字尾自動機學習筆記筆記
- 【筆記】字尾自動機 SAM筆記
- SPOJ 1811 Longest Common Substring(字尾自動機)
- BZOJ4566: [Haoi2016]找相同字元(字尾自動機)字元
- jQuery 郵箱輸入字尾自動補全jQuery
- 字尾陣列複習陣列
- 洛谷P2178 [NOI2015]品酒大會(字尾自動機 線段樹)
- Flutter 開發實戰——自定義省略字尾Flutter
- 字尾陣列學習筆記陣列筆記
- 字尾陣列 學習筆記陣列筆記
- [複習] AC自動機
- 企業計算機中了360字尾勒索病毒怎麼辦,360字尾勒索病毒解密流程計算機解密
- hdu-5384Danganronpa+多校訓練+AC自動機
- AC 自動機學習筆記筆記
- AC自動機學習筆記筆記
- 自動應用廣告藉機器學習之力讓規模化營銷事半功倍機器學習
- 企業計算機中了360字尾勒索病毒如何處理,360字尾勒索病毒處理建議計算機
- Tiki靶機練習
- 靶機練習:Geisha
- 靶機練習:PhotoGrapher
- 字串字尾相關字串
- 字尾陣列,SA陣列
- 歌曲字尾解釋
- 字尾陣列 SA陣列
- 字尾陣列模板陣列
- 自動機器學習簡述(AutoML)機器學習TOML
- win10 怎麼修改字尾_win10如何更改字尾名Win10
- 靶機練習:sunset: midnight
- 【筆記】字尾陣列筆記陣列
- 字尾陣列(後續)陣列
- 前中字尾表示式
- win10怎麼更改字尾名_window10字尾怎麼改Win10
- 五筆:鍵名字練習.txt,250字
- 數字影像處理相關練習
- 筆記本顯示卡字尾Max-Q是什麼意思?關於MAX-Q顯示卡型號字尾MQ含義解析筆記MQ
- 實戰練習之Jsp自定義標籤JS
- 自動機
- 自學linux第一關練習題Linux