P4770 [NOI2018] 你的名字
题目大意
一个长为 \(n(1\le n\le5\cdot10^5)\) 的串 \(s\) , \(q(1\le q\le10^5)\) 次询问,每次询问 \(t\space l\space r(1\le|t|\le5\cdot10^5,\sum|t|\le10^6,1\le l\le r\le n)\) ,求有多少 \(t\) 的本质不同子串,其不是 \(s[l:r]\) 的子串。
思路
考虑对于每个询问,求出 \(t\) 的本质不同子串数,然后再减去 \(t\) 与 \(s[l:r]\) 的本质不同非空公共子串数量即为答案。我们对 \(s\) ,\(t\) 分别建立 \(sam\) 。\(t\) 的本质不同子串个数很容易求出,接下来我们考虑 \(t\) 的每一个前缀,需要找到它的一个最长的后缀,这个后缀为 \(s[l:r]\) 的子串,记这个后缀的长度为 \(len\) ,那么 \(t\) 的这一个前缀中所有长度 \(\le len\) 的后缀就都是 \(s[l:r]\) 的子串,反之则不是。对于 \(t\) 的 \(sam\) 上的每个节点,其对应的 \(len\) 就为其后缀树子树内所有节点 \(len\) 的最大值。对于 \(len\) ,我们可以用 \(t\) 在 \(s\) 的 \(sam\) 上跑一遍匹配来求得,我们用双指针 \(l,r\) 来表示当前待匹配的 \(t\) 子串, \(now\) 为 \(t[l:r-1]\) 在 \(s\) 的 \(sam\) 上匹配的状态,考虑 \(t_r\) 为新加入的字符,如果当前节点有这条边,并且转移过去的状态出现在 \(s[l:r]\) 中,即可直接匹配,对于判断是否出现在 \(s[l:r]\) 中,我们用线段树合并来维护 \(s\) 的 \(sam\) 上每个节点的 \(endpos\) 集合,这样转移时直接进行查询即可判断。如果不能转移,则不断将 \(l+1\) 。过程中要注意检查当前字符串 \(t[l:r-1]\) 的长度,如果小于 \(now\) 的长度,则跳到 \(now\) 在后缀树上的父亲即可。于是我们求出了所有我们需要的信息,最后求解答案即可,复杂度 \(O(|s|+\sum|t|log|s|)\) 。
代码
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mk make_pair
//#define int LL
//#define double LD
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 1000010;
LL ans = 0;
int cnt = 0, root[maxn * 30], N, ed;
struct sgt {
int lc, rc;
}tr[maxn * 30];
int build()
{
++cnt, tr[cnt].lc = tr[cnt].rc = 0;
return cnt;
}
void modify(int p, int l, int r, int val)
{
if (l + 1 == r)
return;
int mid = (l + r) / 2;
if (val < mid)
{
if (!tr[p].lc)
tr[p].lc = build();
modify(tr[p].lc, l, mid, val);
}
else
{
if (!tr[p].rc)
tr[p].rc = build();
modify(tr[p].rc, mid, r, val);
}
}
bool query(int p, int l, int r, int a, int b)
{
if (!p)
return false;
if (l >= a && r <= b)
return true;
int mid = (l + r) / 2;
if (b <= mid)
return query(tr[p].lc, l, mid, a, b);
else if (a >= mid)
return query(tr[p].rc, mid, r, a, b);
else
return query(tr[p].lc, l, mid, a, mid) | query(tr[p].rc, mid, r, mid, b);
}
int merge(int p, int q, int l, int r)
{
if (!p)
return q;
if (!q)
return p;
int ne = build();
if (l + 1 == r)
return ne;
int mid = (l + r) / 2;
tr[ne].lc = merge(tr[p].lc, tr[q].lc, l, mid);
tr[ne].rc = merge(tr[p].rc, tr[q].rc, mid, r);
return ne;
}
int tot = 1, last = 1, leng[maxn * 3];
struct Node {
int len, fa;
int ch[26];
bool isnp;
}sam[maxn * 6];
void extend(int c, int t)
{
int p = last, np = last = ++tot;
sam[np].len = sam[p].len + 1;
sam[np].isnp = true;
if (!t)
root[np] = build(), modify(root[np], 1, N + 1, sam[np].len);
for (; p && !sam[p].ch[c]; p = sam[p].fa)
sam[p].ch[c] = np;
if (!p)
sam[np].fa = (t ? ed + 1 : 1);
else
{
int q = sam[p].ch[c];
if (sam[q].len == sam[p].len + 1)
sam[np].fa = q;
else
{
int nq = ++tot;
sam[nq] = sam[q], sam[nq].len = sam[p].len + 1;
sam[nq].isnp = false;
if (!t)
root[nq] = build();
sam[q].fa = sam[np].fa = nq;
for (; p && sam[p].ch[c] == q; p = sam[p].fa)
sam[p].ch[c] = nq;
}
}
}
vector<int>G[maxn * 3], H[maxn * 3];
void add_edge1(int from, int to)
{
G[from].push_back(to);
}
void add_edge2(int from, int to)
{
H[from].push_back(to);
}
void dfs1(int v)
{
for (auto& to : G[v])
dfs1(to), root[v] = merge(root[v], root[to], 1, N + 1);
}
int dfs2(int v)
{
int num = 0;
if (sam[v].isnp)
num = leng[sam[v].len - 1];
for (auto& to : H[v])
num = max(num, dfs2(to));
if (v != ed + 1)
ans -= max(0, min(num, sam[v].len) - sam[sam[v].fa].len);
return num;
}
string S, T;
int Q;
void match(int a, int b)//[a,b]
{
int l = 0, now = 1;
for (int r = 0; r < T.length(); r++)
{
while (!sam[now].ch[T[r] - 'a'] || !query(root[sam[now].ch[T[r] - 'a']], 1, N + 1, a + r - l, b + 1))
{
l++;
if (l > r)
{
now = 1;
break;
}
if (r - l <= sam[sam[now].fa].len)
now = sam[now].fa;
}
leng[r] = r - l + 1;
if (leng[r])
now = sam[now].ch[T[r] - 'a'];
}
}
void solve()
{
N = S.length(), root[1] = build();
for (int i = 0; i < N; i++)
extend(S[i] - 'a', 0);
ed = tot;
++tot, last = tot;
for (int i = 2; i <= tot; i++)
add_edge1(sam[i].fa, i);
dfs1(1);
int l, r;
for (int i = 1; i <= Q; i++)
{
ans = 0;
cin >> T >> l >> r;
for (int j = 0; j < T.length(); j++)
extend(T[j] - 'a', 1);
for (int j = ed + 2; j <= tot; j++)
ans += (LL)sam[j].len - (LL)sam[sam[j].fa].len;
match(l, r);
for (int j = ed + 1; j <= tot; j++)
H[j].clear();
for (int j = ed + 2; j <= tot; j++)
add_edge2(sam[j].fa, j);
dfs2(ed + 1);
cout << ans << endl;
for (int j = ed + 1; j <= tot; j++)
{
sam[j].fa = sam[j].isnp = sam[j].len = 0;
for (int k = 0; k < 26; k++)
sam[j].ch[k] = 0;
}
tot = ed + 1, last = tot;
}
}
int main()
{
IOS;
cin >> S >> Q;
solve();
return 0;
}

浙公网安备 33010602011771号