阿狸的打字機
題解
題目中給出的字符串就是構建樹的順序.我們將字符串依次讀入,每讀入一個小寫字符就相當於在樹當前節點下插入一個小寫字符,讀入時,就在樹中向父節點移動一步.讀入的時候,就做一個標記.
然後對這顆樹構建自動機.
找找規律發現第串在第串中出現的次數就是樹上串尾點到串末尾點的樹鏈上,所有能直接或間接通過指針跳到串末尾點的點的個數.
觀察到自動機上指針構成了一顆樹.
進一步我們發現,所有能直接或間接跳到串末尾點的點就是樹上,節點的子樹大小.
而實際的答案就是樹上串末尾點的子樹與樹上到的樹鏈的交集中點的個數.
我們對樹做一遍序,用這個序就可以查詢和維護樹的子樹的信息了.
對這個序列建立一個樹狀數組.
然後我們對樹做一遍,在的過程中,把當前點在樹狀數組中的位置,返回的時候在該位置,這樣相當於在樹中從當前點到根節點的樹鏈上所有節點都已經在樹狀數組中激活了,對於當前點作爲的詢問,在樹的以爲根的子樹中計算有多少個點被激活就ok了(樹狀數組查詢前綴和).
代碼
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#define pr(x) std::cout << #x << ':' << x << std::endl
#define rep(i,a,b) for(int i = a;i <= b;++i)
#define LETTER 26
typedef std::pair<int,int> pii;
const int N = 100010;
char s[N];
struct Trie{
int num,fail,fa;
int next[LETTER];
}pool[N];
Trie* const trie = pool + 1;
int cnt;
void init() {
cnt = 0;
memset(pool,0,2*sizeof(Trie));
trie[0].fail = -1;
}
int now;
int pat[N];
int pc;
std::vector<int> trie_edge[N];
void build() {
pc = now = 0;
for(int i = 0;s[i];++i) {
if(s[i] == 'B')
now = trie[now].fa;
else if(s[i] == 'P')
pat[++pc] = now;
else {
int &pos = trie[now].next[s[i]-'a'];
if(!pos) {
pos = ++cnt;
memset(&trie[pos],0,sizeof(Trie));
trie[pos].fa = now;
trie_edge[now].push_back(pos);
}
now = pos;
}
}
std::queue<int> Q;
Q.push(0);
while(!Q.empty()) {
int t = Q.front();Q.pop();
for(int i = 0;i < LETTER;++i) {
int &cur = trie[t].next[i];
if(cur) {
Q.push(cur);
trie[cur].fail = trie[trie[t].fail].next[i];
}
else
cur = trie[trie[t].fail].next[i];
}
}
}
int bitree[N<<1];
int lowbit(int x) {return x & (-x);}
int sum(int pos) {
int res = 0;
for(;pos;pos -= lowbit(pos))
res += bitree[pos];
return res;
}
void add(int pos,int x) {
for(;pos < N<<1;pos += lowbit(pos))
bitree[pos] += x;
}
std::vector<int> edge[N];
int beg[N],end[N];
int idx = 0;
void dfs1(int u){
beg[u] = ++idx;
for(int v : edge[u])
dfs1(v);
end[u] = ++idx;
}
std::vector<pii> query[N];
int ans[N];
void dfs2(int u) {
add(beg[u],1);
for(pii p : query[u]) {
int x = p.first,id = p.second;
ans[id] = sum(end[x])-sum(beg[x]-1);
}
for(int v : trie_edge[u])
dfs2(v);
add(beg[u],-1);
}
int main() {
init();
std::ios::sync_with_stdio(false);
std::cin >> s;
build();
for(int i = 1;i <= cnt;++i) edge[trie[i].fail].push_back(i);
dfs1(0);
int m;
std::cin >> m;
for(int i = 1;i <= m;++i) {
int x,y;
std::cin >> x >> y;
query[pat[y]].push_back((pii){pat[x],i});
}
dfs2(0);
for(int i = 1;i <= m;++i) {
std::cout << ans[i] << std::endl;
}
return 0;
}