洛谷 P5357 【模板】AC自动机(二次加强版)//AC自动机+fail树+差分

题目

题意

n n n个模式串,1个主串,求每个模式串在主串中出现的次数。

思路

暴力 f a i l fail fail跳的话,复杂度 O ( ∣ 主 串 ∣ ∗ ∣ 模 式 串 ∣ ) O(|主串|*|模式串|) O()
考虑将 f a i l fail fail做成一颗以0为根的树。
通过Trie树(已经将模式串尾结点通过 f a i l fail fail指针连到祖先)转移时,每次经过的Trie结点都打上差分标记,相当于 f a i l fail fail树上的 当前节点到根节点的路径上的树结点都+1。
就不需要每次都暴力跳 f a i l fail fail,而是最后统计差分标记。
复杂度 O ( ∣ 主 串 ∣ ) O(|主串|) O()

/*   Author : Rshs
 *   Data : 2019-10-05-17.44
 */
#include
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const double eps = 1e-10;
const LL mod = 1e9+7;
const int MXN = 2e5+1;
/****************************************************************************/
int tr[MXN][26],col[MXN],fail[MXN],nonum;
vector<int>g[MXN];
int cf[MXN],id[MXN];
void ins(char *x,int ii){ //插入
    int le=strlen(x),p=0;
    for(int i=0;i<le;i++){
        int c=x[i]-'a';
        if(!tr[p][c]) tr[p][c]=++nonum;
        p=tr[p][c];
    }
    col[p]=1; //字符串x
    id[ii]=p;
}
void ACGet(){
    queue<int>q;
    memset(fail,0,sizeof(fail));
    for(int i=0;i<26;i++)if(tr[0][i]) q.push(tr[0][i]);//首字符入队,直接将根(0)入队是为了避免指向自己
    while(!q.empty()){
        int now=q.front();q.pop();
        for(int i=0;i<26;i++){
            if(tr[now][i]){
                fail[tr[now][i]]=tr[fail[now]][i];//指向前一个fail(相同最长后缀)
                q.push(tr[now][i]);
            }
            else tr[now][i]=tr[fail[now]][i]; 
        }
    }
}
void query(char *t){
    int p=0;
    for(int i=0;t[i];i++){
        p=tr[p][t[i]-'a'];
        cf[p]++;//每个结点都打上差分标记
    }
}
/****************************************************************************/
char s[200005],t[2000005];
void dfs(int u,int pr){//统计fail树上标记
    for(auto v:g[u]){ 
        if(v==pr)continue;
        dfs(v,u);cf[u]+=cf[v];
    }
}
int main(){
    int n;cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%s",s);ins(s,i);
    }
    ACGet();
    scanf("%s",t);
    query(t);
    for(int i=1;i<=nonum;i++) g[i].push_back(fail[i]),g[fail[i]].push_back(i);
    dfs(0,-1);
    for(int i=1;i<=n;i++)cout<<cf[id[i]]<<'\n';
    return 0;
}

你可能感兴趣的:(洛谷 P5357 【模板】AC自动机(二次加强版)//AC自动机+fail树+差分)