1946 阿狸的打字机
2011年NOI全国竞赛
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机 上只有 28 个按键,分别印有 26 个小写英文字母和'B'、'P'两个字母。 经阿狸研究发现,这个打字机是这样工作的:
输入小写字母,打字机的一个凹槽中会加入这个字母(按 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 个询问的答案。
aPaPBbP
3
1 2
1 3
2 3
2
1
0
1≤n≤ 105,1≤m≤ 105
——————————————————————————————————————————————————————————————————————
N长时间没有更新了,抄了一个!!
AC自动机fail树+DFS序+树状数组
求一个字符串A在另一个字符串B中出现的次数。
首先建立各个串的AC自动机。沿着B串每一个前缀的fail指针走,如果可以走到A串,那么A就是B串的当前前缀的后缀。统计个数就可以了。
但是上面的方法太慢。
可以这样想:既然沿失败指针可以走到A,那么把失败指针反向,再沿着失败指针走可以走到的B串中的点的个数就是答案。
如何统计走到的点中有多少是B串的呢?DFS序+树状数组
因为是字符单个增加,所以当完成B串,B串中的每一个节点对应的位置记为1,则A串末位节点的子树节点的和就是答案。
作出DFS序,用树状数组维护就好了。
确实有点难,看懂了想法还是出了点小问题:树状数组维护的长度应当用dfs序的个数。我的使用的总长度,结果三个点超时。
——————————————————————————————————————————————————————————————————————
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 100005 using namespace std; char s[maxn]; int sz[maxn<<1]; int m; void readint(int &x) { char c=getchar(); for(;c<'0'|| c>'9';c=getchar()); x=0; for(;c>='0' &&c<='9';c=getchar())x=x*10+c-'0'; } inline void add(int p,int x) { for(;p<=l;p+=p&-p)sz[p]+=x; } inline int cha(int p) { int sum=0; for(;p>0;p-=p&-p)sum+=sz[p]; return sum; } int ftu[maxn],ftv[maxn],next[maxn]; int ftjs=0,fth[maxn]; inline void addf(int u,int v) { ftu[++ftjs]=u;ftv[ftjs]=v; next[ftjs]=fth[u];fth[u]=ftjs; } struct ASK { int u,v,id; inline bool operator < (const ASK a)const { return v<a.v; } }ask[maxn]; int ans[maxn]; int l[maxn],r[maxn],dfsx=0; struct AC { int ch[maxn][26],fa[maxn],f[maxn],tot,js,now,cnt,ps[maxn],q[maxn],tail,head; void build() { cnt=now=0,tot=strlen(s); memset(ch[0],0,sizeof(ch[0])); for(int i=0;i<tot;i++) if(s[i]=='P') { ps[++js]=now; } else if(s[i]=='B') now=fa[now]; else { int tp=s[i]-'a'; if(!ch[now][tp]) { memset(ch[++cnt],0,sizeof(ch[cnt])); ch[now][tp]=cnt;fa[cnt]=now; } now=ch[now][tp]; } } void bf() { head=tail=0; for(int i=0;i<26;i++) if(ch[0][i])q[++tail]=ch[0][i]; while(tail>head) { int son,cur=q[++head]; for(int i=0;i<26;i++) if(son=ch[cur][i]) { f[son]=ch[f[cur]][i];q[++tail]=son; } else ch[cur][i]=ch[f[cur]][i]; } } void work() { int jss=0,k=1; now=0; for(int i=0;i<tot;i++) if(s[i]=='P') { for(jss++;ask[k].v==jss && k<=m;k++) //jss第几个结束点 { int pp=ps[ask[k].u]; ans[ask[k].id]= cha(r[pp])-cha(l[pp]-1); } } else if(s[i]=='B') { add(l[now],-1); now=fa[now]; } else { now=ch[now][s[i]-'a']; add(l[now],1); } } }ac; inline void dfs(int u) { l[u]=++dfsx; for(int i=fth[u];i;i=next[i]) dfs(ftv[i]); r[u]=++dfsx; } int main() { scanf("%s",s); ac.build();ac.bf(); for(int i=1;i<=ac.cnt;i++)addf(ac.f[i],i); dfs(0); scanf("%d",&m); for(int i=1;i<=m;i++) { readint(ask[i].u);readint(ask[i].v); ask[i].id=i; //u,v为第几条字符串 } sort(ask+1,ask+m+1); ac.work(); for(int i=1;i<=m;i++)printf("%d ",ans[i]); return 0; }