题意

阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。

经阿狸研究发现,这个打字机是这样工作的:

  • 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。

  • 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。

  • 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。

例如,阿狸输入aPaPBbP,纸上被打印的字符如下:

a

aa

ab

我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其 中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。

阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?

输入格式

输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。

第二行包含一个整数 m,表示询问个数。

接下来 m 行描述所有由小键盘输入的询问。其中第 i 行包含两个整数 x, y,表示第 i 个询问为 (x, y)。

输出格式

输出 m 行,其中第 i 行包含一个整数,表示第 i 个询问的答案。

输入输出样例
输入 #1

aPaPBbP
3
1 2
1 3
2 3

输出 #1

2
1
0

分析

感觉自己字符串题只会依赖哈希很丢人,于是去学了 KMP 和 AC自动机,然后开了这题。。
现在假设我们建完了 AC自动机,得到了 图。
首先我们看一下 的子串的条件是什么,其实是存在 的某个节点,它沿着 指针一直跳,能跳到 的末节点(其实就是 某个前缀的后缀)。 中的出现次数,其实就是 的所有节点中,以 为后缀的节点数,这些节点都可以通过跳 跳到 的末节点。
不妨把 指针和 树的所有节点看成一颗树(这棵树叫树),那么所有能跳到 的点都在 的子树中。
现在的问题就转化为: 的子树中,有多少个点属于
我们把属于 的点记为 ,那么答案就是 的子树和。
子树和,其实就是 序上的前缀和。

然后考虑建 树的过程:

  • 相当于跳到父节点
  • 相当于新打印出一个字符串
  • 其他就是正常建

这样子, 的形态一直都是一条链。
我们考虑离线,求 作为询问串时 的出现次数。
那么我们在搞 的时候:

  • 遇到 ,在 ,然后
  • 遇到 ,说明此时节点为一个字符串 的结束,处理所有询问
  • 对于其他,在

这样对于每一个询问 ,我们只用看 的子树和,然后用树状数组可以快速维护前缀和。
复杂度

代码如下

#include <bits/stdc++.h>
#include<ext/pb_ds/hash_policy.hpp>
#include<ext/pb_ds/assoc_container.hpp>
#define m_p make_pair
#define N 100005
using namespace __gnu_pbds;
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
struct custom_hash {
       static uint64_t splitmix64(uint64_t x) {
           x += 0x9e3779b97f4a7c15;
        x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
        x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
        return x ^ (x >> 31);
    }
    size_t operator()(uint64_t x) const {
        static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
        return splitmix64(x + FIXED_RANDOM);
    }
};
LL z = 1;
int read(){
    int x, f = 1;
    char ch;
    while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') f = -1;
    x = ch - '0';
    while(ch = getchar(), ch >= '0' && ch <= '9') x = x * 10 + ch - 48;
    return x * f;
}
int ksm(int a, int b, int p){
    int s = 1;
    while(b){
        if(b & 1) s = z * s * a % p;
        a = z * a * a % p;
        b >>= 1;
    }
    return s;
}
int ch[N][26], Q[N], h[N], fa[N], v[N], c[N], fail[N], l[N], r[N], ans[N], dfn, cnt, tot, ret;
char s[N];
vector<pair<int, int> > q[N];
struct node{
    int a, b, n;
}d[N * 2];
void add(int i, int x){
    for(; i <= 100000; i += i&-i) c[i] += x;
}
int query(int i){
    int s = 0;
    for(; i > 0; i -= i&-i) s += c[i];
    return s;
}
void cr(int a, int b){
    d[++cnt].a = a; d[cnt].b = b; d[cnt].n = h[a]; h[a] = cnt;
}
void insert(char *s){
    int i, c, n = strlen(s), p = 0;
    for(i = 0; i < n; i++){
        if(s[i] == 'P') Q[++ret] = p;
        else if(s[i] == 'B') p = fa[p];
        else{
            c = s[i] - 'a';
            if(!ch[p][c]) ch[p][c] = ++tot, fa[tot] = p;
            p = ch[p][c];
        }
    }
}
void get_fail(){
    queue<int> q;
    int i, a, j;
    for(i = 0; i < 26; i++) if(ch[0][i]) q.push(ch[0][i]);
    while(!q.empty()){
        a = q.front(); q.pop();
        for(i = 0; i < 26; i++){
            if(ch[a][i]) fail[ch[a][i]] = ch[fail[a]][i], q.push(ch[a][i]);
            else ch[a][i] = ch[fail[a]][i];
        }
    }
}
void dfs(int a, int f){
    int i, b;
    l[a] = ++dfn;
    for(i = h[a]; i; i = d[i].n){
        b = d[i].b;
        if(b == f) continue;
        dfs(b, a);
    }
    r[a] = dfn;
}
void work(char *s){
    int i, j, c, n, p = 0, ret = 0, x, id;
    n = strlen(s);
    for(i = 0; i < n; i++){
        if(s[i] == 'P'){
            ret++;
            for(auto t: q[ret]){
                x = Q[t.first]; id = t.second;
                ans[id] = query(r[x]) - query(l[x] - 1);
            }
        }
        else if(s[i] == 'B') add(l[p], -1), p = fa[p];
        else{
            c = s[i] - 'a';
            if(!ch[p][c]) ch[p][c] = ++tot, fa[tot] = p;
            p = ch[p][c];
            add(l[p], 1);
        }
    }
}
int main(){
    int i, j, n, m, x, y;
    scanf("%s", s);
    insert(s);
    get_fail();
    for(i = 1; i <= tot; i++) cr(fail[i], i);
    dfs(0, 0);
    m = read();
    for(i = 1; i <= m; i++){
        x = read(); y = read();
        q[y].push_back(m_p(x, i));
    }
    memset(ch, 0, sizeof(ch));
    memset(fa, 0, sizeof(fa));
    tot = 0;
    work(s);
    for(i = 1; i <= m; i++) printf("%d\n", ans[i]);
    return 0;
}