Codeforces 1202E You Are Given Some Strings...

本文深入探讨了AC自动机在字符串匹配问题中的应用,通过将多个短串插入AC自动机,实现对长串中所有可能的两两短串组合出现次数的高效计算。文章详细解释了AC自动机的构建过程,包括失败指针的设置和前缀、后缀匹配的原理,并提供了完整的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

题意

给出一个长串 ttt ,n个短串 sis_isi ,问两两连接成的 si+sjs_i+s_jsi+sjttt 的出现次数的和为多少

题解

从另一个角度分析,每次的匹配,在 ttt 中的sis_isisjs_jsj 的连接位置 +1, 我们枚举 ttt 的每一个对应连接的位置,看有多少对能匹配上

若在位置 kkk 能匹配上,有一个特征,si=t[i−len(si)+1,k],sj=t[k+1,i+len(sj)−1]s_i=t[i-len(s_i)+1,k],s_j=t[k+1,i+len(s_j)-1]si=t[ilen(si)+1,k],sj=t[k+1,i+len(sj)1]
就是说,ttt 的后缀等于 sis_isittt 的前缀等于 sjs_jsj
把 n 个串插到 AC 自动机,用 t 匹配就好了,然后所有串翻转,再重复一遍
注意
由于同一种串在不同位置算多次,不能暴力跳 failfailfail , 有一个小优化,就是计算 cntcntcnt 的时候从 failfailfail 继承下来

代码
#include<bits/stdc++.h>
#define N 200010
#define INF 0x3f3f3f3f
#define eps 1e-6
#define pi acos(-1.0)
#define mod 998244353
#define P 1000000007
#define LL long long
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define bug(x) cerr<<#x<<"      :   "<<x<<endl
#define mem(x) memset(x,0,sizeof x)
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)  
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
struct Trie{
    int t[N][26],fail[N],d[N],w[N];
    int tot;
    void init(){ tot=0;
        memset(fail,0,sizeof fail);
        memset(t,0,sizeof t);
        memset(d,0,sizeof d);
    }
    void ins(char* x){
        int i=0;
        for(int j=0;x[j];j++){
            int c=x[j]-'a';
            if(!t[i][c]) t[i][c]=++tot;
            i=t[i][c];
        }
        d[i]++;
    }
    void get_fail(){
        queue<int> q;
        for (int i=0;i<26;i++) if (t[0][i]) q.push(t[0][i]);
        while(!q.empty()) {
            int i=q.front();q.pop();
            for(int j=0;j<26;j++) {
                if (t[i][j]){
                    fail[t[i][j]]=t[fail[i]][j];
                    d[t[i][j]]+=d[fail[t[i][j]]];
                    q.push(t[i][j]);
                }else
                t[i][j]=t[fail[i]][j];
            } 
        }
    }
    void AC_automation(char *x){
        int i=0;
        for(int j=0;x[j];j++) {
            i=t[i][x[j]-'a'];
            w[j]=d[i];
        }
    }
}T1,T2;


char t[N],s[N];

int main(){
    scanf("%s",t);
    int n;
    sc(n);
    while(n--){
        scanf("%s",s);
        T1.ins(s);
        int l=strlen(s);
        reverse(s,s+l);
        T2.ins(s);
    }
    T1.get_fail(); T2.get_fail();

    T1.AC_automation(t);
    
    int l=strlen(t); reverse(t,t+l);
    
    T2.AC_automation(t);
    LL ans=0;
    for(int i=0;i<l-1;i++) ans+=1ll*T1.w[i]*T2.w[l-i-2];
    cout<<ans;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值