洛谷P2414 阿狸的打字机
AC自动机 + 树状数组
这道题暴力把询问离线排序之后,直接用AC自动机找答案可以拿到70分。。
正解应该是考虑fail树和trie树父亲节点的意义。
题目是让我们求第x个单词在第y个中出现了多少次,我们先看第y个单词在trie中的意义。
假设第y个单词在trie中以p节点结尾,那么p节点的父亲节点(包括p节点自己)到根组成的串均为y的前缀。
而这些节点的fail指针指向的节点代表什么呢?假设p的父亲节点a他的fail指针指向b,那么意思是 以b结尾的单词是y这个单词的前缀(根到a是y这个单词的一个前缀)的一个后缀。
前缀的后缀显然就是子串,也就是在y中出现过的串。
那么a这个点的fail树上的祖先节点显然全在y中出现过。
所以我们只要统计每一个y,他在trie中的父亲节点有几个是fail树中x的子节点就行啦。
所以我们先dfs一遍fail树,把fail树映射成序列,再dfs一次trie,对经过的每个节点+1,回溯时-1,计算答案的时候,只要用树状数组辅助查询区间和就好了。
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define full(a, b) memset(a, b, sizeof a)
#define __fastIn ios::sync_with_stdio(false), cin.tie(0)
#define pb push_back
using namespace std;
using LL = long long;
inline int lowbit(int x){ return x & (-x); }
inline int read(){
int ret = 0, w = 0; char ch = 0;
while(!isdigit(ch)){
w |= ch == '-', ch = getchar();
}
while(isdigit(ch)){
ret = (ret << 3) + (ret << 1) + (ch ^ 48);
ch = getchar();
}
return w ? -ret : ret;
}
template <typename A>
inline A __lcm(A a, A b){ return a / __gcd(a, b) * b; }
template <typename A, typename B, typename C>
inline A fpow(A x, B p, C lyd){
A ans = 1;
for(; p; p >>= 1, x = 1LL * x * x % lyd)if(p & 1)ans = 1LL * x * ans % lyd;
return ans;
}
const int N = 100005;
string t;
int n, tot, cnt, trie[N][26], fa[N], ending[N], fail[N], idx[N], k, head[N], dfn[N], c, size[N], tree[N];
int x, y, ans[N], tr[N][26];
struct Edge { int v, next; } edge[N<<1];
vector<pair<int, int>> Q[N];
inline void add(int k, int val){
for(; k <= c; k += lowbit(k)) tree[k] += val;
}
inline int query(int k){
int ret = 0;
for(; k; k -= lowbit(k)) ret += tree[k];
return ret;
}
void addEdge(int a, int b){
edge[k].v = b, edge[k].next = head[a], head[a] = k ++;
}
void getFail(){
queue<int> q;
for(int i = 0; i < 26; i ++){
if(trie[0][i]){
tr[0][i] = trie[0][i];
q.push(trie[0][i]);
}
}
while(!q.empty()){
int s = q.front(); q.pop();
for(int i = 0; i < 26; i ++){
if(trie[s][i]){
tr[s][i] = trie[s][i];
fail[trie[s][i]] = trie[fail[s]][i];
q.push(trie[s][i]);
}
else trie[s][i] = trie[fail[s]][i];
}
}
}
void dfs(int s, int fa){
dfn[s] = ++ c, size[s] = 1;
for(int i = head[s]; i != -1; i = edge[i].next){
int u = edge[i].v;
if(u == fa) continue;
dfs(u, s);
size[s] += size[u];
}
}
void match(int s){
add(dfn[s], 1);
if(ending[s]){
for(auto &p: Q[s]){
int x = p.first, y = p.second;
ans[y] = query(dfn[x] + size[x] - 1) - query(dfn[x] - 1);
}
}
for(int i = 0; i < 26; i ++){
if(tr[s][i]) match(tr[s][i]);
}
add(dfn[s], -1);
}
int main(){
__fastIn;
cin >> t >> n;
int p = 0;
for(int i = 0; i < t.size(); i ++){
if(t[i] >= 'a' && t[i] <= 'z'){
int ch = t[i] - 'a';
if(!trie[p][ch]) trie[p][ch] = ++ tot, fa[tot] = p;
p = trie[p][ch];
}
else if(t[i] == 'B') p = fa[p];
else ending[p] = ++cnt, idx[cnt] = p;
}
getFail();
full(head, -1);
for(int i = 1; i <= tot; i ++){
addEdge(fail[i], i), addEdge(i, fail[i]);
}
dfs(0, 0);
for(int i = 1; i <= n; i ++){
cin >> x >> y;
Q[idx[y]].emplace_back(idx[x], i);
}
match(0);
for(int i = 1; i <= n; i ++){
cout << ans[i] << endl;
}
return 0;
}