大致题意: 给定(n)个模式串,每次询问一个串是多少模式串的子串。
关于(AC)自动机
(n)个模式串?多模匹配问题,显然要上(AC)自动机嘛!
实际上这题也的确可以用(AC)自动机做,这里给出一个此题加强版的链接【BZOJ3881】[COCI2015] Divljak(AC自动机+树状数组)。
不过,由于是在复习后缀自动机,还是决定用后缀自动机写一写这道题。
广义后缀自动机
考虑我们对这(n)个串建后缀自动机,然后对于每一个串,枚举其节点暴力往上跳,给沿途所有点打标记,遇到打过当前串标记的节点就退掉,由于每个串每个点只会被打一次标记,复杂度是正确的。
询问就是询问串在后缀自动机上对应节点被打标记的次数。
发现自己写法居然和bzt惊人相似,好荣幸啊!
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 10000
#define LEN 100000
#define MAX 360000
using namespace std;
int n,l[N+5];char s[LEN+5],ss[MAX+5];
class SuffixAutomation
{
private:
int Nt,lst;struct node {int C,V,L,F,S[30];}O[LEN<<1];
public:
I SuffixAutomation() {Nt=1;}I void Init() {lst=1;}//广义后缀自动机
I void Ins(CI x,CI ti)//插入新字符
{
RI p=lst,now=lst=++Nt;O[now].L=O[p].L+1;
W(p&&!O[p].S[x]) O[p].S[x]=now,p=O[p].F;if(!p) return (void)(O[now].F=1);
RI q=O[p].S[x];if(O[p].L+1==O[q].L) return (void)(O[now].F=q);
RI k=++Nt;(O[k]=O[q]).L=O[p].L+1,O[now].F=O[q].F=k;
W(p&&O[p].S[x]==q) O[p].S[x]=k,p=O[p].F;
}
I void Work(char *s,CI ti)//统计信息
{
RI i,p=1,x;for(i=1;i<=l[ti];++i)//枚举点
{x=p=O[p].S[s[i]&31];W(x&&O[x].V^ti) O[x].V=ti,++O[x].C,x=O[x].F;}//暴力上跳打标记
}
I void Solve(char *s)//求解答案
{
RI p=1;for(RI i=1,l=strlen(s+1);p&&i<=l;++i) p=O[p].S[s[i]&31];//找到对应点
printf("%d
",O[p].C);//返回打标记次数
}
}S;
int main()
{
RI Qt,i,j,tot=0;for(scanf("%d%d",&n,&Qt),i=1;i<=n;++i)//枚举模式串
{
S.Init(),scanf("%s",s+tot+1),l[i]=strlen(s+tot+1);
for(j=1;j<=l[i];++j) S.Ins(s[tot+j]&31,i);tot+=l[i];//建后缀自动机
}
for(tot=0,i=1;i<=n;++i) S.Work(s+tot,i),tot+=l[i];//维护好信息
W(Qt--) scanf("%s",ss+1),S.Solve(ss);return 0;//处理询问
}