# [NOI2011] 阿狸的打字机

题目描述

阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有 28 个按键,分别印有 26 个小写英文字母和 BP 两个字母。经阿狸研究发现,这个打字机是这样工作的:

  • 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
  • 按一下印有 B 的按键,打字机凹槽中最后一个字母会消失。
  • 按一下印有 P 的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。

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

a
aa
ab

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

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

题解

今天才发现以前学的的ac自动机是假的。
这道题我们直接按照按照last数组(fail的优化)往上跳,可以90point。
不过数据是真的水

inline void add(int x,int y,int id){
	++tot;
	to1[tot]=y; to2[tot]=id; nex[tot]=head[x]; head[x]=tot;
}
inline void add1(int x,int y){
	to[++hh]=y; ne[tot]=he[x]; he[x]=hh;  //ne也用的tot
}

这样居然可以90???

说说正解
先说说正确的ac自动机姿势

给你一个文本串 Sn 个模式串 T1n,请你分别求出每个模式串 TiS 中出现的次数。

假设我们当前在ac自动机上走到now这个点,那么一个显然的想法就是沿着fail链往上跳,然后所有走到的点(字符串结尾)+1。

fail[x]=y
反过来想,如果我们将y连向x,那么这个关系将会构成一颗树,我们只需要将字符串到过的点打个标记,最后求一遍子树和即可。

#include<cstdio>
#include<cstring>
#define fo(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
using namespace std;
const int N=2e6+5;
int ch[N][26],fa[N],now,cnt,l,n,c,val[N],mat[N];
char s[N];
int h,t,q[N],r,u,v,f[N];
int head[N],to[N],nex[N],tot,sum[N];
void ins(int x){
	now=0;
	fo(i,1,l){
		c=s[i]-'a';
		if (!ch[now][c]) ch[now][c]=++cnt;
		now=ch[now][c];
	}
	mat[x]=now;
}
void build(){
	h=t=0;
	fo(i,0,25) if (ch[0][i]) q[++t]=ch[0][i];
	while (h<t){
		r=q[++h];
		fo(i,0,25){
			u=ch[r][i];
			if (!u) {
				ch[r][i]=ch[f[r]][i];//与下一个代码有区别
				continue; 
			}
			v=f[r];
			while (v && !ch[v][i]) v=f[v];
			f[u]=ch[v][i];
			q[++t]=u;
		}
	}
}
void add(int x,int y){
	to[++tot]=y; nex[tot]=head[x]; head[x]=tot;
}
void dfs(int x){
	for (int i=head[x];i;i=nex[i]){
		int v=to[i];
		dfs(v);
		sum[x]+=sum[v];
	}
}
int main(){
//	freopen("data.in","r",stdin);
	scanf("%d",&n);
	fo(i,1,n){
		scanf("%s",s+1);
		l=strlen(s+1);

		ins(i);
	}
	build();
	scanf("%s",s+1);
	l=strlen(s+1);
	
	now=0;
	
	fo(i,1,l) {
		c=s[i]-'a';
		now=ch[now][c];
		sum[now]++;
	}
	
	fo(i,1,cnt) add(f[i],i);
	dfs(0);
	
	fo(i,1,n) {
		printf("%d\n",sum[mat[i]]);
	}
	return 0;
} 

那么这个题其实也差不多。
首先建好树后先得到dfs序,一个字树的dfs序肯定是一段连续的区间。我们只需要dfs的时候修改即可。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for (register int (i)=(a);(i)<=(b);(i)++)
using namespace std;
const int N=1e5+1000;
int ch[N][26];
char s[N];
int n,m,x,y,now,l,c,cnt;
int f[N],fa,val[N],sum;
int h,t,q[N],r,u,v,ans[N],d[N],Fa[N],wh[N],dep;
bool vis[N];
int to[N],ne[N],head[N],hh;
int nex[N],to1[N],to2[N],he[N],tot;
int e[N],st[N],top,k;
int To[N],num,Nex[N],last[N];
int dfn[N],zz;
int li[N],ri[N];
int ss[N];
int lowbit(int x){
	return (x&(-x));
}
void update(int x,int y){
	while (x<N){
		ss[x]+=y;
		x+=lowbit(x);
	}
}
int ask(int x){
	int s=0;
	while (x){
		s+=ss[x];
		x-=lowbit(x);
	}
	return s;

}

inline void add(int x,int y,int id){
	++tot;
	to1[tot]=y; to2[tot]=id; nex[tot]=head[x]; head[x]=tot;
}
inline void add1(int x,int y){
	to[++hh]=y; ne[hh]=he[x]; he[x]=hh;
}
void add2(int x,int y){
	To[++num]=y; Nex[num]=last[x]; last[x]=num;
}
inline void R(int &x){
	int t=0; char ch;
	for (ch=getchar();!('0'<=ch && ch<='9');ch=getchar());
	for (;('0'<=ch && ch<='9');ch=getchar()) t=t*10+ch-'0';
	x=t;
}
void work(){
	now=0;  dep=0;
	
	l=strlen(s+1); 
	fo(i,1,l) {
		if ('a'<=s[i] && s[i]<='z') {
			fa=now;
			dep++;
			
			c=s[i]-'a';
			if (!ch[now][c]) ch[now][c]=++cnt; 
			now=ch[now][c] ; 
			
			Fa[now]=fa;
			
		}
		if (s[i]=='B') {
			dep--;
			now=Fa[now];
		}
		if (s[i]=='P') {
			++sum;
			add1(now,sum);
			wh[sum]=now;
			
			val[now]=1;
		}
	}
}
void build(){
	h=t=0; 
	fo(i,0,25) if (ch[0][i]) q[++t]=ch[0][i];
	while (h<t) {
		r=q[++h];
		fo(i,0,25){
			u=ch[r][i];
			if (!u) continue; // 区别
			v=f[r];
			while (v && !ch[v][i]) v=f[v];
			f[u]=ch[v][i];
			q[++t]=u;
		}
	}
}


void Dfs(int x){
	dfn[x]=++zz;
	li[x]=ri[x]=dfn[x];

	for (int i=last[x];i;i=Nex[i]){
		int v=To[i];
		Dfs(v);
		li[x]=min(li[x],li[v]);
		ri[x]=max(ri[x],ri[v]);
	}
}
void dg(int x){
	if (vis[x]) return;
	vis[x]=1;
	update(dfn[x],1);
	
	for (int i=head[x];i;i=nex[i]){
		d[to2[i]]=ask(ri[to1[i]])-ask(li[to1[i]]-1); 
	}
	
	fo(i,0,25) {
		if (ch[x][i]) dg(ch[x][i]);
	}

	update(dfn[x],-1);
}
int main(){
//	freopen("data.in","r",stdin);
//	freopen("data.out","w",stdout);
	scanf("%s",s+1);
	work();
	build();

	scanf("%d",&m);
	fo(i,1,m) {
		R(x); R(y);
		add(wh[y],wh[x],i);
	}
	
	fo(i,1,cnt) add2(f[i],i);
	
	Dfs(0);
	dg(0);
	
	fo(i,1,m)  printf("%d\n",d[i]);
	return 0;
}

两者其实是有区别的。
前者是找一个字符串,所以可以连这种虚拟边,但是这道题目我的方法需按照trie上dfs进行,如果连上这些边后会导致不能按照正确的顺序访问。
例:

aPaPBbPaPBBBbaPaPBBbPaP
1
1 6

posted @   gan_coder  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
  1. 1 青春コンプレックス 結束バンド
  2. 2 ギターと孤独と蒼い惑星 結束バンド
  3. 3 星座になれたら 結束バンド
  4. 4 恋のうた (feat. 由崎司) 鬼頭明里
  5. 5 月と星空 カノエラナ
  6. 6 Cagayake!GIRLS[5人Ver.] (TV size Ver.) 桜高軽音部
  7. 7 不可思議のカルテ 不可思議のカルテ
  8. 8 優しさの理由 ChouCho
ギターと孤独と蒼い惑星 - 結束バンド
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : ZAQ

作曲 : 音羽-otoha-

编曲 : ak?kin

突然降る夕立 あぁ傘もないや嫌

突然下起的夕雨 让我受够自己没带雨伞

空のご機嫌なんか知らない

天空的心情如何又与我何干

季節の変わり目の服は 何着りゃいいんだろ

季节交替之时 该换上什么衣服呢

春と秋 どこいっちゃったんだよ

春天与秋天 究竟去了哪里呢

息も出来ない 情報の圧力

息も出来ない 情報の圧力

息も出来ない 情報の圧力

令人喘不过气来 信息时代的压力

めまいの螺旋だ わたしはどこにいる

在这眩晕的螺旋之中 我究竟身处何方

こんなに こんなに 息の音がするのに

明明有着那么多 那么多的呼吸声

変だね 世界の音がしない

奇怪的是 这世界却 悄然无声

足りない 足りない 誰にも気づかれない

足りない 足りない 誰にも気づかれない

足りない 足りない 誰にも気づかれない

还不够 还不够 我还未被任何人所注意

殴り書きみたいな音 出せない状態で叫んだよ

好似杂乱无章的音律 不成声地呐喊着

「ありのまま」なんて 誰に見せるんだ

「真实的自我」 又是为了让谁看见

馬鹿なわたしは歌うだけ

愚昧无知的我唯有放声高歌

ぶちまけちゃおうか 星に

倾诉一切吧 对那星辰

エリクサーに張り替える作業もなんとなくなんだ

エリクサーに張り替える作業もなんとなくなんだ

エリクサーに張り替える作業もなんとなくなんだ

即便换上伊利克斯琴弦 音色也不尽人意

欠けた爪を少し触る

轻抚那边缘缺损的指甲

半径300mmの体で 必死に嗚いてる

以这半径300mm的身躯拼命奏响吉他

音楽にとっちゃ ココが地球だな

对于音乐来说这副身躯便是地球啊

空気を握って 空を殴るよ

空気を握って 空を殴るよ

空気を握って 空を殴るよ

握紧空气 殴向天空

なんにも起きない わたしは無力さ

却无事发生 我是如此的无力啊

だけどさ その手で この鉄を弾いたら

但是啊 用这手弹起这铁弦时

何かが変わって見えた…ような。

就好像能看到有什么发生变化…一样啊。

眩しい 眩しい そんなに光るなよ

眩しい 眩しい そんなに光るなよ

眩しい 眩しい そんなに光るなよ

闪烁耀眼 光彩夺目 请不要如此明亮啊

わたしのダサい影が より色濃くなってしまうだろ

这不就只会让我的丑陋影子愈加明显吗

なんでこんな熱くなっちゃってんだ 止まんない

为何我内心会变得如此炽热 无法停止

馬鹿なわたしは歌うだけ

愚昧无知的我唯有放声高歌

うるさいんだって 心臓

鼓动愈加喧嚣的 心脏

蒼い惑星 ひとりぼっち

蒼い惑星 ひとりぼっち

蒼い惑星 ひとりぼっち

我在这蓝色星球上孤独一人

いっぱいの音を聞いてきた

聆听到了众多声音

回り続けて 幾億年

地球自转数亿年

一瞬でもいいから…ああ

所以一瞬也好啊…啊啊

聞いて

请聆听

聴けよ

聴けよ

聴けよ

聆听我的声音啊

わたし わたし わたしはここにいる

わたし わたし わたしはここにいる

わたし わたし わたしはここにいる

我啊 我啊 我就存在于此啊

殴り書きみたいな音 出せない状態で叫んだよ

好似杂乱无章的音律 不成声地呐喊着

なんかになりたい なりたい 何者かでいい

仅是想成为 想成为 无论什么人都好

馬鹿なわたしは歌うだけ

愚昧无知的我唯有放声高歌

ぶちまけちゃおうか 星に倾诉一切吧 对那星辰

点击右上角即可分享
微信分享提示