HDU 7064. Singing Superstar

链接

https://acm.hdu.edu.cn/showproblem.php?pid=7064

题意

$T$ $(T\le 5)$ 个测试样例,主串 $s$ 长度不超过 $1e5$,每次有 $n$ 个询问,每次询问一个长度不超过 $30$ 的字符串 $a$,求其在主串中最多不重叠出现了多少次。$(\sum{|s|}\le 4e5,\sum{|a|}\le 4e5)$

思路

对 $n$ 个串建立 AC 自动机,遍历主串在AC 自动机上走,每次暴力往上跳 fail 指针,记录每个节点最后出现的位置,如果不重叠就 $+1$。

代码

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
#include <bits/stdc++.h>
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(),(x).end()
#define PB push_back
#define EB emplace_back
#define MP make_pair
#define FI first
#define SE second
using namespace std;
typedef double DB;
typedef long double LD;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
typedef vector<int> VI;
typedef vector<PII> VPII;
// head
const int N=3e5+5;
struct AC {
int tr[N][26],fail[N],len[N],id[N],last[N],tot[N],sz; // fail 指针指向与当前前缀匹配的最长后缀的位置
void init() {
for(int i=0;i<=sz;i++) {
for(int j=0;j<26;j++) tr[i][j]=0;
fail[i]=tot[i]=0;
}
sz=0;
}
void insert(const string &s,int i) {
int u=0;
for(auto c:s) {
int v=c-'a';
if(!tr[u][v]) {
tr[u][v]=++sz;
len[sz]=len[u]+1;
last[sz]=0xc0c0c0c0;
}
u=tr[u][v];
}
id[i]=u;
}
void build() {
queue<int> q;
for(int v=0;v<26;v++) if(tr[0][v]) q.push(tr[0][v]);
while(SZ(q)) {
int u=q.front();
q.pop();
for(int v=0;v<26;v++) {
if(tr[u][v]) fail[tr[u][v]]=tr[fail[u]][v],q.push(tr[u][v]);
else tr[u][v]=tr[fail[u]][v];
}
}
}
} ac;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;cin>>T;
while(T--) {
ac.init();
string s;cin>>s;
int n;cin>>n;
for(int i=1;i<=n;i++) {
string t;cin>>t;
ac.insert(t,i);
}
ac.build();
int u=0;
for(int i=0;i<SZ(s);i++) {
int v=s[i]-'a';
u=ac.tr[u][v];
int t=u;
while(t) {
if(ac.last[t]+ac.len[t]<=i) {
++ac.tot[t];
ac.last[t]=i;
}
t=ac.fail[t];
}
}
for(int i=1;i<=n;i++) cout<<ac.tot[ac.id[i]]<<'\n';
}
return 0;
}