字符串杂题
Trick
- 对于子串问题,考虑转化成后缀的前缀或者前缀的后缀进行处理。(A task for substrings)
题目
[OOI 2023] A task for substrings
询问 \(T\) 的子串是不好处理的,考虑变成前缀或者后缀问题。
对于一个询问 \([l,r]\),考虑找到一个串 \(S_i\),记它匹配到 \(T\) 上的区间是 \([L,R]\),满足 \(L\in[1,l),R\in[l,r]\),且 \(R\) 最大。
那么所有匹配结束位置在 \((R,r]\) 的串都在 \([l,r]\) 内,可以直接算答案,剩下的只需要考虑串 \(S_i\) 的子串。
也就是对于串 \(S_i\) 的后缀的一个前缀,要满足匹配的左端点要大于等于 \(l\),对这样的串计数。
这些都可以用 AC 自动机完成。
对于找 \(S_i\),可以对所有 \(S\) 建立 AC 自动机,然后在 \(T\) 上匹配,在线段树上维护出每个匹配结束位置的最小左端点,查寻 \(S_i\) 就可以线段树上二分。
在对所有 \(S_i\) 的反串建立 AC 自动机,然后对每个 \(S\) 的后缀进行匹配,计算一个前缀和即可。
实现很麻烦,稍微抄了下题解。
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e6 + 10, M = 5e6 + 10, inf = 1e9;
int n, m;
string s[N], t;
int lent, len[N], sum[N];
int tot;
int fail[N], tr[26][N], val[N], id[N];
int rev[M];
ll pre[M], ans[M];
vector< int> G[N];
void init() {
for ( int i = 0; i <= tot; i ++) {
fail[i] = val[i] = id[i] = 0;
G[i].clear();
for ( int j = 0; j < 26; j ++)
tr[j][i] = 0;
}
tot = 0;
}
void insert( string s, int id, int op) {
int u = 0;
if (! op)
for ( int i = 1; i <= len[id]; i ++) {
int & v = tr[s[i] - 'a'][u];
if (! v) v = ++ tot;
u = v;
}
else
for ( int i = len[id]; i; i --) {
int & v = tr[s[i] - 'a'][u];
if (! v) v = ++ tot;
u = v;
}
val[u] = 1, :: id[u] = id;
}
void build() {
queue< int> q;
for ( int i = 0; i < 26; i ++)
if (tr[i][0]) q.push(tr[i][0]);
while (q.size()) {
int u = q.front(); q.pop();
for ( int i = 0; i < 26; i ++) {
int & v = tr[i][u];
if (v) {
fail[v] = tr[i][fail[u]];
q.push(v);
} else v = tr[i][fail[u]];
}
}
for ( int i = 1; i <= tot; i ++)
G[fail[i]].push_back(i);
}
void dfs( int u, int sum, int id) {
if (! :: id[u]) :: id[u] = id;
val[u] += sum;
for ( auto v : G[u]) dfs(v, val[u], :: id[u]);
}
struct sgt {
#define ls k << 1
#define rs k << 1 | 1
#define mid ((l + r) >> 1)
int mi[M * 4];
void build( int k = 1, int l = 1, int r = lent) {
if (l == r) return mi[k] = l - len[rev[l]] + 1, void();
build(ls, l, mid), build(rs, mid + 1, r);
mi[k] = min(mi[ls], mi[rs]);
}
int find( int x, int y, int v, int k = 1, int l = 1, int r = lent) {
if (l > y || r < x || mi[k] >= v) return 0;
if (l == r) return l;
int res = find(x, y, v, rs, mid + 1, r);
if (! res) res = find(x, y, v, ls, l, mid);
return res;
}
#undef ls
#undef rs
#undef mid
} T;
signed main() {
ios :: sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m;
cin >> t; t = " " + t;
lent = (int)t.size() - 1;
for ( int i = 1; i <= n; i ++) {
cin >> s[i]; len[i] = (int)s[i].size(), sum[i] = sum[i - 1] + len[i];
s[i] = " " + s[i];
insert(s[i], i, 0);
}
build();
dfs(0, 0, 0);
int u = 0;
for ( int i = 1; i <= lent; i ++) {
u = tr[t[i] - 'a'][u];
rev[i] = id[u], pre[i] = pre[i - 1] + val[u];
}
T.build();
init();
for ( int i = 1; i <= n; i ++)
insert(s[i], i, 1);
build(), dfs(0, 0, 0);
for ( int o = 1; o <= n; o ++) {
int u = 0;
for ( int i = len[o]; i; i --) {
int id = sum[o] - i + 1;
if (i < len[o]) ans[id] = ans[id - 1];
u = tr[s[o][i] - 'a'][u];
ans[id] += val[u];
}
}
while (m --) {
int l, r; cin >> l >> r;
int pos = T.find(l, r, l);
if (! pos) {
cout << pre[r] - pre[l - 1] << ' ';
} else {
cout << pre[r] - pre[pos] + ans[sum[rev[pos] - 1] + pos - l + 1] << ' ';
}
}
return 0;
}

浙公网安备 33010602011771号