【總結】AC自動機處理的一類查詢(bzo3881Divljak+bzoj2780Sevenk Love Oimaster+bzoj2754喵星球上的點名)
AC自動機
AC自動機是解決多模板匹配問題的演算法。
它的優點在於思路易懂,程式碼簡潔,可以線上性時間內求解。 缺點則在於必須要先知道所有的模板,而實際運用中很多情況無法預先知道需要查詢的模板串。
AC自動機相關的題目有一些明顯的特徵: 文字串較長或文字串唯一/較少,多模式串匹配,可以離線,模式串總長有一定限制等。 而處理問題時也需要先離線建立所有模式串的AC自動機。
AC自動機常結合樹(fail樹)及dp(匹配方案/概率)考察。這篇總結主要寫寫關於fail樹的一些操作。
匹配問題
串匹配的查詢有以下幾種基本形式:
文字串匹配
- 查詢單個文字串中所有模式串出現的次數(同一個模式串出現多次算多次)
對於AC自動機的每個結點構造一個陣列,表示以結點結束的模式串個數。在構造鏈時可以利用處理出陣列,表示結點在樹中到根結點路徑上所有點的之和。
當文字串匹配到結點時,答案加上即可。複雜度為 為文字串長,為模式串總長。
- 查詢單個文字串中出現的模式串的個數(同一個模式串出現多次算一次)
首先處理出樹的序。
考慮在1.中,每匹配到一個點就將答案加上,而其中會有些點被重複計算了多次,而算重的部分為一些點到根路徑的共同路徑。故將匹配過程中到達的所有結點取出,按序排序,加上每個點的,減去相鄰兩點的。複雜度多了個排序的。
- 查詢一組文字串(多個)中出現的模式串的個數(同一個模式串在這組文字串中出現多次算一次)
同2.,不過現在是在一組文字串匹配結束後,再取出結點排序處理。
模式串匹配
- 分別查詢所有模式串在多個文字串中出現的次數(在一個文字串中出現多次算多次)
首先將所有模式串建立AC自動機並記錄每個串對應的結束結點。處理出樹的序和每個結點的子樹大小。
逐個將文字串插入AC自動機,每匹配到一個結點,將其值加1。可以發現一個串出現次數即為樹中其子樹結點之和。
每個點子樹在序中必然是連續的一段,答案區間求和,考慮構造BIT,可以在結點個數的時間求解。複雜度
- 分別查詢所有模式串在多個文字串中出現的次數(在一個文字串中出現多次算一次)
逐個插入文字串,同樣考慮將每次匹配到的結點取出,按序排序,相鄰兩點的減1。複雜度還是的。
- 分別查詢所有模式串在多組文字串中出現的次數(在一組文字串中出現多次算一次)
同2.,不過現在是在一組文字串匹配結束後,再取出結點排序處理。
下面三道題即為以上查詢的簡單應用。
模式串匹配 2.
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;
int n,qr,df[N],top[N],f[N],sz[N],son[N],d[N];
int bit[N],pos[100010],dfn,pr;
int head[N],to[N<<1],nxt[N<<1],tot;
char s[N],SS[70];
queue<int>Q;
char cp;
inline void rd(int &x)
{
cp=getchar();x=0;
for(;!isdigit(cp);cp=getchar());
for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48);
}
inline void ot(int x)
{
int re=0;
for(;x;x/=10) SS[++re]='0'+x%10;
if(!re) putchar('0');
for(;re;--re) putchar(SS[re]);
}
inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
inline bool cmp(const int&A,const int&B){return df[A]<df[B];}
void dfs(int x)
{
int i,j;sz[x]=1;son[x]=-1;
for(i=head[x];i;i=nxt[i]){
j=to[i];if(j==f[x]) continue;
d[j]=d[x]+1;dfs(j);sz[x]+=sz[j];
if((son[x]==-1) || sz[j]>sz[son[x]]) son[x]=j;
}
}
void dfss(int x,int tpo)
{
df[x]=++dfn;top[x]=tpo;
if(son[x]==-1) return;
dfss(son[x],tpo);
for(int j,i=head[x];i;i=nxt[i]){
j=to[i];if(j==f[x] || j==son[x]) continue;
dfss(j,j);
}
}
inline int LCA(int x,int y)
{
for(;top[x]!=top[y];x=f[top[x]])
if(top[x]==0 || d[f[top[x]]]<d[f[top[y]]]) swap(x,y);
if(d[x]<d[y]) swap(x,y);
return y;
}
inline void modify(int x,int vv)
{for(;x<=dfn;x+=(x&(-x))) bit[x]+=vv;}
inline int query(int x)
{
int re=0;
for(;x;x-=(x&(-x)))
re+=bit[x];
return re;
}
inline void upp(int x)
{
for(;~x;x=f[top[x]]){
modify(df[top[x]],pr);
if(df[x]<dfn) modify(df[x]+1,-pr);
}
}
struct AC{
queue<int>Q;
int vs[N],cot,len,ch[N][26],cnt;
inline void ins(int id,char *s)
{
int i,j,alp,u=0;len=strlen(s);
for(i=0;i<len;++i){
alp=s[i]-'a';
if(!ch[u][alp]) ch[u][alp]=++cnt;
u=ch[u][alp];
}
pos[id]=u;
}
void getfail()
{
int i,j,x,y,z,u;
for(i=0;i<26;++i){
x=ch[0][i];if(x) Q.push(x);
}
for(;!Q.empty();){
x=Q.front();Q.pop();y=f[x];
lk(x,y);lk(y,x);
for(i=0;i<26;++i){
z=ch[x][i];u=ch[y][i];
if(!z) {ch[x][i]=u;continue;}
f[z]=u;Q.push(z);
}
}
}
inline void ad(char *s)
{
int i,j,alp,u=0,x;cot=0;len=strlen(s);
for(i=0;i<len;++i){
u=ch[u][s[i]-'a'];
vs[++cot]=u;
}
sort(vs+1,vs+cot+1,cmp);
cot=unique(vs+1,vs+cot+1)-vs-1;
pr=1;upp(vs[1]);
for(i=2;i<=cot;++i){
pr=1;upp(vs[i]);pr=-1;
upp(LCA(vs[i-1],vs[i]));
}
}
}ac;
int main(){
int i,j,op,x;
rd(n);f[0]=-1;
for(i=1;i<=n;++i){scanf("%s",s);ac.ins(i,s);}
ac.getfail();d[0]=1;dfs(0);dfss(0,0);
rd(qr);
for(;qr;--qr){
rd(op);
if(op==1){
scanf("%s",s);ac.ad(s);
}else{
rd(x);
ot(query(df[pos[x]]));putchar('\n');
}
}
return 0;
}
模式串匹配 2.
#include<bits/stdc++.h>
using namespace std;
const int N=360010;
int n,qr,df[N],top[N],f[N],sz[N],son[N],d[N];
int bit[N],pos[60010],dfn;
int head[N],to[N<<1],nxt[N<<1],tot;
char t[N],SS[70];
string s[10010];
char cp;
inline void rd(int &x)
{
cp=getchar();x=0;
for(;!isdigit(cp);cp=getchar());
for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48);
}
inline void ot(int x)
{
int re=0;
for(;x;x/=10) SS[++re]='0'+x%10;
if(!re) putchar('0');
for(;re;--re) putchar(SS[re]);
}
inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
inline bool cmp(const int&A,const int&B){return df[A]<df[B];}
void dfs(int x)
{
int i,j;sz[x]=1;son[x]=-1;
for(i=head[x];i;i=nxt[i]){
j=to[i];if(j==f[x]) continue;
d[j]=d[x]+1;dfs(j);sz[x]+=sz[j];
if((son[x]==-1) || sz[j]>sz[son[x]]) son[x]=j;
}
}
void dfss(int x,int tpo)
{
df[x]=++dfn;top[x]=tpo;
if(son[x]==-1) return;
dfss(son[x],tpo);
for(int j,i=head[x];i;i=nxt[i]){
j=to[i];if(j==f[x] || j==son[x]) continue;
dfss(j,j);
}
}
inline int LCA(int x,int y)
{
for(;top[x]!=top[y];x=f[top[x]])
if(top[x]==0 || d[f[top[x]]]<d[f[top[y]]]) swap(x,y);
if(d[x]<d[y]) swap(x,y);
return y;
}
inline void modify(int x,int vv)
{for(;x<=dfn;x+=(x&(-x))) bit[x]+=vv;}
inline int query(int x)
{
int re=0;
for(;x;x-=(x&(-x)))
re+=bit[x];
return re;
}
struct AC{
queue<int>Q;
int vs[N],cot,len,ch[N][26],cnt;
inline void ins(int id,char *s)
{
int i,j,alp,u=0;len=strlen(s);
for(i=0;i<len;++i){
alp=s[i]-'a';
if(!ch[u][alp]) ch[u][alp]=++cnt;
u=ch[u][alp];
}
pos[id]=u;
}
void getfail()
{
int i,j,x,y,z,u;
for(i=0;i<26;++i){
x=ch[0][i];if(x) Q.push(x);
}
for(;!Q.empty();){
x=Q.front();Q.pop();y=f[x];
lk(x,y);lk(y,x);
for(i=0;i<26;++i){
z=ch[x][i];u=ch[y][i];
if(!z) {ch[x][i]=u;continue;}
f[z]=u;Q.push(z);
}
}
}
inline void ad(string s)
{
int i,j,alp,u=0,x;cot=0;
len=s.size();
for(i=0;i<len;++i){
u=ch[u][s[i]-'a'];
if(u) vs[++cot]=u;
}
if(!cot) return;
sort(vs+1,vs+cot+1,cmp);
cot=unique(vs+1,vs+cot+1)-vs-1;
modify(df[vs[1]],1);
for(i=2;i<=cot;++i){
modify(df[vs[i]],1);
modify(df[LCA(vs[i-1],vs[i])],-1);
}
}
}ac;
int main(){
int i,j,op,x;
rd(n);rd(qr);
for(i=1;i<=n;++i) cin>>s[i];
for(i=1;i<=qr;++i){scanf("%s",t);ac.ins(i,t);}
ac.getfail();
d[0]=1;f[0]=-1;dfs(0);dfss(0,0);
for(i=1;i<=n;++i) ac.ad(s[i]);
for(i=1;i<=qr;++i){
x=pos[i];
ot((query(df[x]+sz[x]-1)-query(df[x]-1)));
putchar('\n');
}
return 0;
}
模式串匹配 3. + 文字串匹配 3.
#include<bits/stdc++.h>
#define itr map<int,int>::iterator
#define fi first
#define sc second
using namespace std;
const int N=1e5+10;
int n,m,df[N],top[N],f[N],sz[N],son[N],d[N];
int bit[N],pos[N],ans[50005],dfn,nwss;
int ed[N],sum[N];
int head[N],to[N<<1],nxt[N<<1],tot;
vector<int>nam[50005][2];
char cp,SS[70];
inline int rd()
{
cp=getchar();int x=0;
for(;!isdigit(cp);cp=getchar());
for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48);
return x;
}
inline void ot(int x)
{
int re=0;
for(;x;x/=10) SS[++re]='0'+x%10;
if(!re) putchar('0');
for(;re;--re) putchar(SS[re]);
}
inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
inline bool cmp(const int&A,const int&B){return df[A]<df[B];}
void dfs(int x)
{
int i,j;sz[x]=1;son[x]=-1;
for(i=head[x];i;i=nxt[i]){
j=to[i];if(j==f[x]) continue;
d[j]=d[x]+1;sum[j]=sum[x]+ed[j];dfs(j);sz[x]+=sz[j];
if((son[x]==-1) || sz[j]>sz[son[x]]) son[x]=j;
}
}
void dfss(int x,int tpo)
{
df[x]=++dfn;top[x]=tpo;
if(son[x]==-1) return;
dfss(son[x],tpo);
for(int j,i=head[x];i;i=nxt[i]){
j=to[i];if(j==f[x] || j==son[x]) continue;
dfss(j,j);
}
}
inline int LCA(int x,int y)
{
for(;top[x]!=top[y];x=f[top[x]])
if(top[x]==0 || d[f[top[x]]]<d[f[top[y]]]) swap(x,y);
if(d[x]<d[y]) swap(x,y);
return y;
}
inline void modify(int x,int vv)
{for(;x<=dfn;x+=(x&(-x))) bit[x]+=vv;}
inline int query(int x)
{
int re=0;
for(;x;x-=(x&(-x))) re+=bit[x];
return re;
}
struct AC{
queue<int>Q;
map<int,int>ch[N];
int vs[N<<1],cot,len,cnt;
inline void ins(int id)
{
int alp,u=0;
for(len=rd();len;--len){
alp=rd();
if(!ch[u][alp]) ch[u][alp]=++cnt,u=cnt;
else u=ch[u][alp];
}
ed[u]++;pos[id]=u;
}
void getfail()
{
int x,y,z,v,u;itr i;
for(i=ch[0].begin();i!=ch[0].end();++i) Q.push(i->sc);
for(;!Q.empty();){
x=Q.front();Q.pop();y=f[x];
lk(x,y);lk(y,x);
for(i=ch[x].begin();i!=ch[x].end();++i){
for(u=i->fi,z=y;z && (!ch[z][u]);z=f[z]);
v=ch[z][u];f[i->sc]=v;Q.push(i->sc);
}
}
}
inline void cal(int id)
{
int i,j,alp,u=0,z;nwss=cot=0;
for(j=0;j<2;++j){
u=0;
for(len=nam[id][j].size(),i=0;i<len;++i){
alp=nam[id][j][i];
if(!ch[u][alp]){
for(z=f[u];z && (!ch[z][alp]);z=f[z]);
u=ch[z][alp];
}else u=ch[u][alp];
if(u) vs[++cot]=u;
}
}
if(!cot) return;
sort(vs+1,vs+cot+1,cmp);
cot=unique(vs+1,vs+cot+1)-vs-1;
modify(df[vs[1]],1);nwss+=sum[vs[1]];
for(i=2;i<=cot;++i){
z=LCA(vs[i-1],vs[i]);
nwss-=sum[z];nwss+=sum[vs[i]];
modify(df[vs[i]],1);modify(df[z],-1);
}
ans[id]=nwss;
}
}ac;
int main(){
int i,j,x;
n=rd();m=rd();
for(i=1;i<=n;++i)
for(j=0;j<2;++j)
for(x=rd();x;--x)
nam[i][j].push_back(rd());
for(i=1;i<=m;++i) ac.ins(i);
ac.getfail();
d[0]=1;f[0]=-1;dfs(0);dfss(0,0);
for(f[0]=0,i=1;i<=n;++i) ac.cal(i);
for(i=1;i<=m;++i){
x=pos[i];
ot(query(df[x]+sz[x]-1)-query(df[x]-1));
putchar('\n');
}
for(i=1;i<=n;++i)
ot(ans[i]),putchar(i==n?'\n':' ');
return 0;
}