链接
题解
很巧妙的一个题。
首先给出的字符串几乎明示了用 trie,因为后面要匹配所以建AC自动机也是很自然的操作。然后每次询问第 x x x 个串在第 y y y 个串中出现的次数,暴力匹配显然不行,考虑利用 fail 树的性质,标记所有 y y y 串的结点然后统计 x x x 串结尾结点的子树和。
但是每次做一遍树形DP依然是不行的,需要进一步转化问题。我们可以离线处理查询,对于每一个 y y y 串求出所有对应的询问,求出 fail 树的 DFS 序后用维护这个序列,遍历 trie 的时候是单点修改,求子树和的时候是区间查询,所以用树状数组来维护。
代码
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mkp make_pair
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
const int maxn = 1e5 + 9;
struct tnode
{
int ch[26];
int cnt, fail;
};
tnode tr[maxn];
string s;
int trietot, fa[maxn], word[maxn], wordtot;
void ins()
{
int now = 0;
for (auto i : s)
{
if (i == 'P')
{
tr[now].cnt++;
word[++wordtot] = now;
}
else if (i == 'B')
{
now = fa[now];
}
else
{
int c = i - 'a';
if (!tr[now].ch[c])
{
tr[now].ch[c] = ++trietot;
fa[trietot] = now;
}
now = tr[now].ch[c];
}
}
}
void build()
{
queue<int> q;
for (int i = 0; i < 26; ++i)
{
if (tr[0].ch[i])
{
tr[tr[0].ch[i]].fail = 0;
q.push(tr[0].ch[i]);
}
}
while (!q.empty())
{
int x = q.front();
q.pop();
for (int i = 0; i < 26; ++i)
{
if (tr[x].ch[i])
{
tr[tr[x].ch[i]].fail = tr[tr[x].fail].ch[i];
q.push(tr[x].ch[i]);
}
else
{
tr[x].ch[i] = tr[tr[x].fail].ch[i];
}
}
}
}
struct node
{
int next, to;
node() {}
node(int n, int t) : next(n), to(t) {}
};
node edg[maxn << 1];
int head[maxn], nodetot;
int q, dfn[maxn], dfstime, last[maxn];
void addedge(int from, int to)
{
edg[++nodetot] = node(head[from], to);
head[from] = nodetot;
}
inline int lowbit(int x) { return x & (-x); }
int bit[maxn];
void update(int pos, int k)
{
for (int i = pos; i <= dfstime; i += lowbit(i))
bit[i] += k;
}
int ask(int pos)
{
int ret = 0;
for (int i = pos; i; i -= lowbit(i))
ret += bit[i];
return ret;
}
vector<pii> qry[maxn];
void prework(int u)
{
dfn[u] = ++dfstime;
for (int i = head[u]; i; i = edg[i].next)
{
int to = edg[i].to;
prework(to);
}
last[u] = dfstime;
}
int ans[maxn];
void work()
{
int cnt = 0, root = 0;
for (int i = 0; i < s.length(); ++i)
{
if (s[i] == 'P')
{
cnt++;
for (int j = 0; j < qry[cnt].size(); ++j)
{
int x = word[qry[cnt][j].first];
int y = qry[cnt][j].second;
ans[y] = ask(last[x]) - ask(dfn[x] - 1);
}
}
else if (s[i] == 'B')
{
update(dfn[root], -1);
root = fa[root];
}
else
{
root = tr[root].ch[s[i] - 'a'];
update(dfn[root], 1);
}
}
for (int i = 1; i <= q; ++i)
cout << ans[i] << '\n';
}
signed main()
{
cin >> s;
ins();
build();
for (int i = 1; i <= trietot; ++i)
addedge(tr[i].fail, i);
cin >> q;
for (int i = 1, x, y; i <= q; ++i)
{
cin >> x >> y;
qry[y].pb(mkp(x, i));
}
prework(0);
work();
return 0;
}