ac自动机是基于字典树和fail指针来快速一类解决多串匹配的问题。
先用所有模式串建一个字典树。然后用bfs搞出每个节点的fail指针。
fail指针是指向 和当前 前缀的后缀有最长匹配的前缀。
扫文本串的时候,标记一下访问过的字典树上的节点。。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define wzh(x) cerr<<#x<<' '<<x<<endl
int n;
char a[N],b[N];
struct AC{
int cnt=1,fa[N],ta[N],tr[N][26];
queue<int>q;
void init(){
cnt=1;
}
void clear(int o){
ta[o]=0;
for(int i=0;i<26;i++){
if(tr[o][i]){
clear(tr[o][i]);
tr[o][i]=0;
}
}
}
void insert(char *a,int n){
int o=1;
for(int i=1;i<=n;i++){
if(!tr[o][a[i]-'a']){
tr[o][a[i]-'a']=++cnt;
}
o=tr[o][a[i]-'a'];
}
ta[o]++;
}
void build(){
q.push(1);
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=0;i<26;i++){
if(tr[x][i]){
if(x==1){
fa[tr[x][i]]=1;
}else{
int f=fa[x];
while(f!=1 &&!tr[f][i]){
f=fa[f];
}
if(tr[f][i])fa[tr[x][i]]=tr[f][i];
else fa[tr[x][i]]=1;
}
q.push(tr[x][i]);
}
}
}
// for(int i=1;i<=cnt;i++)cout<<i<<' '<<fa[i]<<endl;
}
int solve(char *a,int n){
int res=0;
int o=1;
for(int i=1;i<=n;i++){
while(o!=1 && !tr[o][a[i]-'a']){
o=fa[o];
}
if(tr[o][a[i]-'a'])o=tr[o][a[i]-'a'];
else o=1;
int f=o;
while(ta[f]!=-1){
res+=ta[f];
ta[f]=-1;
f=fa[f];
}
}
return res;
}
}g;
int main() {
scanf("%d",&n);
g.cnt=1;g.fa[1]=1;
for(int i=1;i<=n;i++){
scanf("%s",a+1);
g.insert(a,strlen(a+1));
}
g.build();
scanf("%s",b+1);
printf("%d\n",g.solve(b,strlen(b+1)));
return 0;
}
洛谷3796
因为这个模式串的深度最多70,就是说字典树的深度不超过70.
那扫文本串的时候可以直接暴力跳fail,记录访问到的点访问的次数。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define wzh(x) cerr<<#x<<' '<<x<<endl
struct AC{
int cnt=1,fa[N],ta[N],tr[N][26];
int vis[N];
pair<int,char>pre[N];
queue<int>q;
void init(){
cnt=1;
}
void clear(int o){
ta[o]=0;vis[o]=0;
for(int i=0;i<26;i++){
if(tr[o][i]){
clear(tr[o][i]);
tr[o][i]=0;
}
}
}
int insert(char *a,int n,int now){
int o=1;
for(int i=1;i<=n;i++){
if(!tr[o][a[i]-'a']){
tr[o][a[i]-'a']=++cnt;
}
o=tr[o][a[i]-'a'];
}
ta[o]++;
return o;
}
void build(){
q.push(1);
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=0;i<26;i++){
if(tr[x][i]){
if(x==1){
fa[tr[x][i]]=1;
}else{
int f=fa[x];
while(f!=1 &&!tr[f][i]){
f=fa[f];
}
if(tr[f][i])fa[tr[x][i]]=tr[f][i];
else fa[tr[x][i]]=1;
}
q.push(tr[x][i]);
}
}
}
}
void solve(char *a,int n){
int o=1;
for(int i=1;i<=n;i++){
while(o!=1 && !tr[o][a[i]-'a']){
o=fa[o];
}
if(tr[o][a[i]-'a'])o=tr[o][a[i]-'a'];
else o=1;
int f=o;
while(f!=1){
vis[f]++;
f=fa[f];
}
}
}
}g;
int n,d[N],c[N];
char a[155][77],b[N];
int main() {
ios::sync_with_stdio(false);
while(cin>>n){
if(!n)break;g.cnt=1;g.fa[1]=1;
for(int i=1;i<=n;i++){
cin>>a[i]+1;
c[i]=g.insert(a[i],strlen(a[i]+1),i);
d[i]=i;
}
g.build();
cin>>b+1;
g.solve(b,strlen(b+1));
sort(d+1,d+1+n,[](int x,int y){
if(g.vis[c[x]]==g.vis[c[y]])return x<y;
return g.vis[c[x]]>g.vis[c[y]];
});
int l=1;
while(l+1<=n&&g.vis[c[d[l+1]]]==g.vis[c[d[l]]])l++;
cout<<g.vis[c[d[1]]]<<'\n';
for(int i=1;i<=l;i++){
cout<<a[d[i]]+1<<'\n';
assert(d[i]>d[i-1]);
}
g.clear(1);
}
return 0;
}
就在fail树上求一下子树和即可。。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e6 + 10;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define wzh(x) cerr<<#x<<' '<<x<<endl
struct AC{
int cnt=1,fa[N],ta[N],tr[N][26];
queue<int>q;
int sz[N];
vector<int>v[N];
void init(){
cnt=1;
}
void clear(int o){
ta[o]=0;
for(int i=0;i<26;i++){
if(tr[o][i]){
clear(tr[o][i]);
tr[o][i]=0;
}
}
}
int insert(char *a,int n){
int o=1;
for(int i=1;i<=n;i++){
// cout<<i<<endl;
if(!tr[o][a[i]-'a']){
tr[o][a[i]-'a']=++cnt;
}
sz[cnt]=0;
o=tr[o][a[i]-'a'];
}
return o;
}
void build(){
q.push(1);
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=0;i<26;i++){
if(tr[x][i]){
if(x==1){
fa[tr[x][i]]=1;
}else{
int f=fa[x];
while(f!=1 &&!tr[f][i]){
f=fa[f];
}
if(tr[f][i])fa[tr[x][i]]=tr[f][i];
else fa[tr[x][i]]=1;
}
v[fa[tr[x][i]]].pb(tr[x][i]);
q.push(tr[x][i]);
}
}
}
}
void solve(char *a,int n){
int o=1;
for(int i=1;i<=n;i++){
while(o!=1 && !tr[o][a[i]-'a']){
o=fa[o];
}
if(tr[o][a[i]-'a'])o=tr[o][a[i]-'a'];
else o=1;
int f=o;
sz[f]++;
}
}
int get(int x){
for(auto k:v[x]){
get(k);
sz[x]+=sz[k];
}
}
}g;
int n,ti[N];
char a[N],b[N];
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",a+1);
ti[i]=g.insert(a,strlen(a+1));
}
g.build();
scanf("%s",b+1);
g.solve(b,strlen(b+1));
// cout<<"ok"<<endl;
g.get(1);
for(int i=1;i<=n;i++)printf("%d\n",g.sz[ti[i]]);
return 0;
}