Description
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有 个按键,分别印有
个小写英文字母和 B 、 P 两个字母。 经阿狸研究发现,这个打字机是这样工作的:
- 输入小写字母,打字机的一个凹槽中会加入这个字母(按 P 前凹槽中至少有一个字母)。
- 按一下印有 B 的按键,打字机凹槽中最后一个字母会消失。
- 按一下印有 P 的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失(保证凹槽中至少有一个字母)。
例如,阿狸输入 aPaPBbP ,纸上被打印的字符如下:
a aa ab
我们把纸上打印出来的字符串从 开始顺序编号,一直到
。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数
(其中
),打字机会显示第
个打印的字符串在第
个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
Input
输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。
第二行包含一个整数 ,表示询问个数。 接下来
行描述所有由小键盘输入的询问。其中第i行包含两个整数
,表示第i个询问为
。
Output
输出 行,其中第
行包含一个整数,表示第
个询问的答案。
Sample Input
aPaPBbP 3 1 2 1 3 2 3
Sample Output
3
HINT
所有测试数据的范围和特点如下表所示:
| 测试点编号 | 字符串长度 | 输入总长 (输入文件第一行的字符数) | ||
|---|---|---|---|---|
| 1 | - | |||
| 2 | - | |||
| 3 | 单个长度 | |||
| 4 | 单个长度 | |||
| 5 | 总长度 | |||
| 6 | 总长度 | |||
| 7 | 总长度 | |||
| 8 | - | |||
| 9 | - | |||
| 10 | - |
Solution
force
对于每一个询问按照排序一遍,然后相同的
用同义词遍历的solve得到的
数组统计答案即可。
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<map>
#include<bitset>
#include<queue>
#define mk make_pair
#define fi first
#define nd second
#define pii pair<int,int>
#define pb push_back
#define sqr(x) ((x)*(x))
using namespace std;
typedef long long ll;
inline ll read() {ll x = 0; char ch = getchar(), w = 1;while(ch < '0' || ch > '9') {if(ch == '-') w = -1;
ch = getchar();}while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0';ch = getchar();}return x * w;}
void write(ll x) {if(x < 0) putchar('-'), x = -x;if(x > 9) write(x / 10);putchar(x % 10 + '0');}
inline void writeln(ll x) {write(x);puts("");}
const int N = 1e6;
char s[N];
int n;
int ch[N][26], tot, fa[N], pos[N];
int cnt[N], m;
vector<int> v[N];
int fail[N];
void getfail() {
queue<int>q;
for(int i = 0; i < 26; ++i) if(ch[0][i]) q.push(ch[0][i]);
while(!q.empty()) {
int x = q.front(); q.pop();
for(int i = 0; i < 26; ++i) {
if(ch[x][i]) {
fail[ch[x][i]] = ch[fail[x]][i];
q.push(ch[x][i]);
}
else ch[x][i] = ch[fail[x]][i];
}
}
}
struct TT {
int x, y, i;
}t[N];
bool cmp(TT _x, TT _y) {
return _x.y < _y.y;
}
int sum[N], st[N], top, ans[N];
bool vis[N];
void solve(int x) {
x = pos[x];
while(x) {
for(int j = x; j; j = fail[j]) for(int k = 0; k < v[j].size(); ++k) {
++sum[v[j][k]];
if(!vis[v[j][k]]) {
vis[v[j][k]] = 1;
st[++top] = v[j][k];
}
}
x = fa[x];
}
}
int main() {
int now = 0;
scanf("%s",s + 1);
n = strlen(s + 1);
for(int i = 1; i <= n; ++i) {
if(s[i] >= 'a' && s[i] <= 'z') {
int to = s[i] - 'a';
if(!ch[now][to]) ch[now][to] = ++tot, fa[tot] = now;
now = ch[now][to];
}
if(s[i] == 'B') now = fa[now];
if(s[i] == 'P')
v[now].pb(++m), pos[m] = now;
}
getfail();
int Q = read();
for(int i = 1; i <= Q; ++i)
t[i].x= read(), t[i].y = read(), t[i].i= i;
sort(t +1, t +1 +Q, cmp);
for(int i = 1, j; i <= Q; i = j) {
j = i;
solve(t[i].y);
while(t[j].y == t[i].y) {
ans[t[j].i] = sum[t[j].x];
++j;
}
while(top) {
vis[st[top]] = 0;
sum[st[top--]] = 0;
}
}
for(int i = 1; i <= Q; ++i)
writeln(ans[i]);
return 0;
} std
对于询问,求
在
里出现过几次,相当于在原来
树上跑
这条链,然后跑链上的每一个节点的
来统计答案。实际上就是枚举
的前缀,然后找前缀的后缀就是找
了。
换一句话说对于一个循环就是要从
出发经过
边一共可以到达多少个
链上的点。
我们可以模拟一遍很显然当前到达的节点即
到其路径的链,所以直接用树状数组查询即可。
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<map>
#include<bitset>
#include<queue>
#define mk make_pair
#define fi first
#define nd second
#define pii pair<int,int>
#define pb push_back
#define sqr(x) ((x)*(x))
using namespace std;
typedef long long ll;
inline ll read() {ll x = 0; char ch = getchar(), w = 1;while(ch < '0' || ch > '9') {if(ch == '-') w = -1;
ch = getchar();}while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0';ch = getchar();}return x * w;}
void write(ll x) {if(x < 0) putchar('-'), x = -x;if(x > 9) write(x / 10);putchar(x % 10 + '0');}
inline void writeln(ll x) {write(x);puts("");}
const int N = 1e6;
char s[N];
int n;
int ch[N][26], tot, fa[N], pos[N], CH[N][26];
int cnt[N], m;
vector<int> v[N];
int fail[N];
void getfail() {
queue<int>q;
for(int i = 0; i < 26; ++i) if(ch[0][i]) q.push(ch[0][i]);
while(!q.empty()) {
int x = q.front(); q.pop();
for(int i = 0; i < 26; ++i) {
if(ch[x][i]) {
fail[ch[x][i]] = ch[fail[x]][i];
q.push(ch[x][i]);
}
else ch[x][i] = ch[fail[x]][i];
}
}
}
struct TT {
int x, y, i;
}t[N];
bool cmp(TT _x, TT _y) {
return _x.y < _y.y;
}
struct Edge {
int u, v, nxt;
}e[N * 2];
int head[N], en;
void addedge(int x, int y) {
e[++en].u = x, e[en].v = y,e[en].nxt = head[x], head[x] = en;
}
int L[N], R[N], num;
void DFS(int x) {
L[x] = ++num;
for(int i = head[x]; i;i =e[i].nxt) DFS(e[i].v);
R[x] = num;
}
int c[N];
void add(int x, int v) {
for(; x <= num; x += x & -x)
c[x] += v;
}
int query(int x) {
int res = 0;
for(; x; x -= x & -x)
res += c[x];
return res;
}
int ans[N];
int ql[N], qr[N];
void solve(int x) {
add(L[x], 1);
if(ql[x]) {
for(int i = ql[x]; i <= qr[x]; ++i) {
ans[t[i].i] = query(R[t[i].x]) - query(L[t[i].x] - 1);
}
}
for(int i = 0; i < 26; ++i)
if(CH[x][i]) solve(CH[x][i]);
add(L[x], -1);
}
int main() {
int now = 0;
scanf("%s",s + 1);
n = strlen(s + 1);
for(int i = 1; i <= n; ++i) {
if(s[i] >= 'a' && s[i] <= 'z') {
int to = s[i] - 'a';
if(!ch[now][to]) ch[now][to] = ++tot, fa[tot] = now;
now = ch[now][to];
}
if(s[i] == 'B') now = fa[now];
if(s[i] == 'P')
v[now].pb(++m), pos[m] = now;
}
for(int i = 0; i <= tot; ++i) memmove(CH[i], ch[i], sizeof ch[i]);
getfail();
for(int i = 1; i <= tot; ++i) addedge(fail[i], i);
DFS(0);
int Q = read();
for(int i = 1; i <= Q; ++i)
t[i].x= read(), t[i].y = read(), t[i].i= i;
sort(t +1, t +1 +Q, cmp);
for(int i = 1; i <= Q; ++i) {
t[i].x = pos[t[i].x];
t[i].y = pos[t[i].y];
}
for(int i = 1, j; i <= Q; i = j) {
j = i;
ql[t[i].y] = i;
while(t[j].y== t[i].y) ++j;
qr[t[i].y] = j - 1;
}
solve(0);
for(int i = 1; i <= Q; ++i)
writeln(ans[i]);
return 0;
} 
京公网安备 11010502036488号