阿狸的打字机

#include <map>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib> 
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
#define ULL unsigned long long
#define PII pair <int, int>
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); ++i)
#define per(i,j,k) for (int i = (j); i >= (k); --i)

template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
template <typename T>
void read (T &x) {
    x = 0; T f = 1;
    char ch = getchar ();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar ();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar ();
    }
    x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... args) {
    read (x); read (args...);
}
char For_Print[25];
template <typename T>
void write (T x) {
    if (x == 0) { putchar ('0'); return; }
    if (x < 0) { putchar ('-'); x = -x; }
    int poi = 0;
    while (x) {
        For_Print[++poi] = x % 10 + '0';
        x /= 10;
    }
    while (poi) putchar (For_Print[poi--]);
}
template <typename T>
void print (T x, char ch) {
    write (x); putchar (ch);
}

const LL Mod = 1e9 + 7;

void del (LL &x, LL y) { ((x -= y) < 0) && (x += Mod); }
void add (LL &x, LL y) { ((x += y) >= Mod) && (x -= Mod); }

// 一些乱七八糟的模板

const int Maxn = 1e5;
const int Maxkind = 26;

int m;
char s[Maxn + 5];
PII a[Maxn + 5];

///*
int fa[Maxn + 5]; // fa[p] 记录 p 在 Trie 树上的父亲 
int cnt, Tire[Maxkind + 5][Maxn + 5]; // Tire[0][p] 表示 p 的 fail 边
int id[Maxn + 5];
void Work (char *str) {
	int len = strlen (str + 1);
	int p = 0, tot = 0;
	rep (i, 1, len) {
		if (s[i] == 'P') 
			id[++tot] = p;
			// 遇到打印
			// id: 第 tot 个打印的字符串,在 Trie 树上最后一个字符的位置是 id[tot] 
		else if (s[i] == 'B')
			p = fa[p];
			// 删除,在 Trie 树上回退到父亲 
		else {
			int ch = s[i] - 'a' + 1;
			if (!Tire[ch][p])
				Tire[ch][p] = ++cnt;
			fa[cnt] = p;
			p = Tire[ch][p];
			// 加字符,拓展 Trie 树 
		}
	}
}
// 跑出 Trie 树 
void Get_Next () {
    queue <int> q;
    rep (i, 1, Maxkind) if (Tire[i][0]) q.push (Tire[i][0]);
    while (q.size ()) {
        int u = q.front (); q.pop ();
        rep (ch, 1, Maxkind) {
            int v = Tire[ch][u];
            if (v != 0) {
                Tire[0][v] = Tire[ch][Tire[0][u]];
                q.push (v);
            }
            else {
            	Tire[ch][u] = Tire[ch][Tire[0][u]];
			}
        }
    }
}
// 跑出 fail 数组以及自动机。
//*/
//~~为什么写了这么多~~ 

///*
int timestamp, dfn[Maxn + 5];
int Size[Maxn + 5];
vector <int> g[Maxn + 5];
void add (int x, int y) {
	g[x].push_back (y);
}
void dfs (int u) {
	Size[u] = 1; dfn[u] = ++timestamp;
	for (auto v : g[u]) {
		dfs (v);
		Size[u] += Size[v];
	}
}
//*/ // 跑出 fail树的 dfn 序 

///*
#define ls (p << 1)
#define rs (p << 1 | 1)
#define lp (Tr[p].l)
#define rp (Tr[p].r)
#define midp ((lp + rp) >> 1)
#define sump (Tr[p].sum)
struct Segment_Tree {
	int l, r;
	int sum;
}Tr[(Maxn << 2) + 5];
void Build (int p, int l, int r) {
	sump = 0; lp = l; rp = r;
	if (l == r) {
		return;
	}
	Build (ls, l, midp);
	Build (rs, midp + 1, r);
}
void Push_Up (int p) {
	sump = Tr[ls].sum + Tr[rs].sum;
}
void Update (int p, int Index, int x) {
	if (lp == rp) {
		sump += x;
		return;
	}
	if (Index <= midp) Update (ls, Index, x);
	else Update (rs, Index, x);
	Push_Up (p);
}
int Query (int p, int l, int r) {
	if (l <= lp && rp <= r) {
		return sump;
	}
	int res = 0;
	if (l <= midp) res += Query (ls, l, r);
	if (r > midp) res += Query (rs, l, r);
	return res;
}
//*/ // 板上板的线段树,支持单点修改和区间查询 

///*
int ans[Maxn + 5];
vector <PII> quest[Maxn + 5];
bool vis[Maxn + 5];
void Calc (char *str) {
	rep (i, 1, cnt)
		add (Tire[0][i], i);
	dfs (0); Build (1, 1, cnt + 1);
	// 初始化以及建 fail 树 
	
	int len = strlen (str + 1), p = 0;
	rep (i, 1, len) {
		if (s[i] == 'P')
			continue;
		else if (s[i] == 'B') {
			// 回到父亲,链的贡献要减去 p 号点 
			Update (1, dfn[p], -1);
			p = fa[p];
			continue;
		}
		else {
			p = Tire[s[i] - 'a' + 1][p];
			Update (1, dfn[p], 1);
			// 向下走,链的贡献要加上 p 号点 
			if (vis[p]) continue; // 不重复离线计算答案,防止多次走到同一个节点导致超时 
			vis[p] = 1;
			for (auto j : quest[p]) {
				ans[j.se] = Query (1, dfn[j.fi], dfn[j.fi] + Size[j.fi] - 1);
				// 贡献是当前链在 j.fi 子树内的节点个数,所以子树的 dfn 序为 [dfn[j.fi], dfn[j.fi] + Size[j.fi] - 1] 
			}
			// 离线计算答案
		}
	}
}
//*/

int main () {
// 	freopen ("D:\\lihan\\1.in", "r", stdin);
//	freopen ("D:\\lihan\\1.out", "w", stdout);

	cin >> (s + 1);
	cin >> m;
	rep (i, 1, m) {
		cin >> a[i].fi >> a[i].se;
	}
	
	Work (s);
	Get_Next ();
	rep (i, 1, m) {
		quest[id[a[i].se]].push_back (MP (id[a[i].fi], i));
	}
	Calc (s);
	
	rep (i, 1, m) cout << ans[i] << endl;
    return 0;
}
posted @ 2022-01-24 09:51  C2022lihan  阅读(9)  评论(0编辑  收藏  举报