阿狸的打字机

阿狸的打字机

首先建立fail树,考虑离线询问。考虑怎么用ACAM处理一个串在另一个串的出现次数,

可以给询问按照主串分类,对于每一个主串分别处理所有询问。某一个串在主串中出现,主串中位于这个串最后的那个位置跳fail必然可以跳到这个串。所以可以树上差分搞一下

大概没啥问题?


然后写了一发,就70了。

注意到对于100%的数据,没有总长度的限制啊!

但是由于有$n$的限制,trie中的总点数是很小的。

处理方法类似 CF1207G

  • 在将串加入trie的时候,可以记录一个now表示到现在为止加入的串对应在trie上的节点。如果现在要退格,这个点就向上跳,否则在print的时候就往下插入就好了。这样不难发现插入的时候复杂度是线性的。
  • 在处理询问的时候,可以把询问挂在trie上,然后dfs一遍trie处理所有询问。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>
#include<assert.h>
using namespace std;
#define MAXN 1000006
#define mod 1000000007
int n , m;
int len;
char ch[MAXN] , zh[MAXN]; int en;
int son[MAXN][26] , fa[MAXN] , fail[MAXN] , num[MAXN] , idx , to[MAXN];
int cid = 0;

int head1[MAXN] , tt1[MAXN << 1] , nex1[MAXN << 1] , ecn1;
void ade1( int u , int v ) {
// cout << u << ' ' << v << endl;
tt1[++ ecn1] = v , nex1[ecn1] = head1[u] , head1[u] = ecn1;
}

int now = 0 , tim = 0;
void ins( char* ch , int id ) {
// cout << ch << endl;
for( int i = 0 ; i < len ; ++ i ) {
// printf("%d\n",++tim);
if( !son[now][ch[i] - 'a'] ) son[now][ch[i] - 'a'] = ++ idx , fa[idx] = now , ade1( now , idx );
now = son[now][ch[i] - 'a'];
}
++ num[now] , to[id] = now;
}

int head[MAXN] , tt[MAXN << 1] , nex[MAXN << 1] , ecn;
void ade( int u , int v ) {
// cout << u << ' ' << v << endl;
tt[++ ecn] = v , nex[ecn] = head[u] , head[u] = ecn;
}

void build( ) {
queue<int> Q;
for( int i = 0 ; i < 26 ; ++ i ) if( son[0][i] ) Q.push( son[0][i] );
while( !Q.empty() ) {
int cur = Q.front( ); Q.pop( );
ade( fail[cur] , cur );
for( int i = 0 ; i < 26 ; ++ i )
if( son[cur][i] )
fail[son[cur][i]] = son[fail[cur]][i] , Q.push( son[cur][i] );
else
son[cur][i] = son[fail[cur]][i];
}
}
struct que {
int t , s , id;
} Q[MAXN] ;
bool cmp( que a , que b ) {
return a.s < b.s;
}
int dfn[MAXN] , R[MAXN] , clo;
void dfs( int u , int fa ) {
dfn[u] = ++ clo;
for( int i = head[u] ; i ; i = nex[i] ) {
int v = tt[i];
dfs( v , u );
}
R[u] = clo;
}

namespace fwk {
int T[MAXN];
void add( int u , int c ) {
assert( u );
while( u < MAXN ) T[u] += c , u += ( u & -u );
}
int sum( int u ) {
int ret = 0;
while( u > 0 ) ret += T[u] , u -= ( u & -u );
return ret;
}
}
vector<pair<int,int> > ques[MAXN];
int ans[MAXN];
void dfs1( int u ) {
fwk::add( dfn[u] , 1 );
for( int i = 0 ; i < ques[u].size() ; ++ i )
ans[ques[u][i].second] = fwk::sum( R[ques[u][i].first] ) - fwk::sum( dfn[ques[u][i].first] - 1 );
for( int i = head1[u] ; i ; i = nex1[i] ) {
int v = tt1[i];
dfs1( v );
}
fwk::add( dfn[u] , -1 );
}

int main( ) {
// freopen("2.in","r",stdin);
scanf("%s",ch); n = strlen( ch );
for( int i = 0 ; i < n ; ++ i ) {
if( ch[i] == 'P' ) ins( zh , ++ cid ) , len = 0;
else if( ch[i] == 'B' ) {
if( len ) -- len;
else now = fa[now];
}
else zh[len ++] = ch[i];
}
build( );
dfs( 0 , 0 );
cin >> m;
for( int i = 1 , t , s ; i <= m ; ++ i )
scanf("%d%d",&t ,&s ) , ques[to[s]].push_back( make_pair( to[t] , i ) );
dfs1( 0 );
// sort( Q + 1 , Q + 1 + m , cmp );
for( int i = 1 ; i <= m ; ++ i ) printf("%d\n",ans[i]);
}
文章作者: yijan
文章链接: https://yijan.co/old20/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog