首页 > 其他分享 >2023CCPC女生专场 L 字符串游戏【AC自动机】

2023CCPC女生专场 L 字符串游戏【AC自动机】

时间:2023-10-27 13:23:56浏览次数:49  
标签:AC int sum len 2023CCPC fail 自动机

一句话题解:AC自动机,在fail树上自顶向下预处理,以实现O(1)统计答案

Description:

n个模式串{Sn},1个文本串T。每次小B会选取T的一个子串(只要子串位置不相同则视作不同),对答案的贡献是该子串中含有的模式串的总数目。对于选取子串的所有方法,求总共的答案。

Solution:

对于文本串出现的任何一个模式串,假设它的位置是\([T_l,T_{l+1},...,T_r]\),则对答案的贡献是\(l*(len_T-r+1)\)

暴力的想法是:对模式串建立Trie树,枚举文本串T的每一个位置作为起始位置,在Trie树上匹配,找到模式串就累计答案。

由于是多模式匹配问题,考虑AC自动机。对模式串建立AC自动机,对于文本串T的每一个位置,在AC自动机上查找,由于查找的是T的前缀的不同后缀,所以结束位置\(T _r\)是不变的,则答案的贡献

\(\sum [l_i*(len_T-r+1)]\\=(\sum l_i)*(len_T-r+1)\\=[\sum (r-len_{s_i}+1)]*(len_T-r+1)\\=[(r+1)\sum1-\sum len_{s_i}]*(len_T-r+1)\)

(注意上面的式子建立在字符串的下标从1开始的基础上)。

所以,只需要统计每个结点能跳到的fail结点的数量\(\sum 1\),以及len的总和\(\sum len_i\),就可以\(O(1)\)得到以T的某一个位置结尾的所有答案。

将fail看作父亲结点,则能跳到的所有的fail结点就是所有的祖先结点,则预处理出所有的\(\sum 1\)和\(\sum len_i\)复杂度是\(O(\sum len_s)\)的。

综上所述,时间复杂度是\(O(\sum|S|*N+|T|)\),\(N\)是字符集大小。

Little Insight: 在AC自动机的拓扑排序优化中,答案是在fail树上自底向上统计的;在本题中,先从上到下预处理,贡献统一在最深的结点加上。

Code:

//AC自动机,在fail树上自顶向下预处理,以实现O(1)统计答案
//by dttttttt
//2023/10/27
#include<iostream>
#include<queue>
#include<vector>
#define ll long long
using namespace std;
const int N=2e5+5,M=1e6+5,mod=1e9+7;
int n,m,tot;
string s[N],t[M];
struct node{
    int to[30];
    int fail,end;
    int s_num; //存\sum 1
    ll s_len; //存\sum len
    vector<int> fail_to; //fail树上的子节点
}ac[N];
void build_trie(){
    for(int j=1;j<=n;++j){
        int cur=0;
        int len_j=s[j].length();
        for(int i=0;i<len_j;++i){
            int c=s[j][i]-'a';
            if(ac[cur].to[c]==0) ac[cur].to[c]=++tot;
            cur=ac[cur].to[c];
        }
        ac[cur].end=s[j].length();
    }
}
void build_fail(){
    queue<int>q;
    ac[0].fail=0;
    for(int i=0;i<26;++i)
        if(ac[0].to[i]){
            ac[ac[0].to[i]].fail=0;
            ac[0].fail_to.push_back(ac[0].to[i]);
            q.push(ac[0].to[i]);
        }
    
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=0;i<26;++i){
            if(ac[u].to[i]){
                ac[ac[u].to[i]].fail=ac[ac[u].fail].to[i];
                q.push(ac[u].to[i]);//这里老是忘写
                ac[ac[ac[u].fail].to[i]].fail_to.push_back(ac[u].to[i]);
            }
            else{
                ac[u].to[i]=ac[ac[u].fail].to[i];
                //ac[ac[ac[u].fail].to[i]].fail_to.push_back(ac[u].to[i]); 这里不需要!
            }
        }
    }
}
void dfs_fail(int u,int num,ll len){
    if(ac[u].end){
        num+=1;
        len+=ac[u].end;
        len%=mod;
    }
    ac[u].s_num=num;
    ac[u].s_len=len;
    for(int v:ac[u].fail_to){
        dfs_fail(v,num,len);
    }
}

int main(){
    ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;++i) cin>>s[i];
    for(int i=1;i<=m;++i) cin>>t[i];

    build_trie();
    build_fail();

    dfs_fail(0,0,0); //dfs fail树预处理

    for(int j=1;j<=m;++j){
        int len_t=t[j].length();
        int cur=0;
        ll ans=0;
        for(int i=0;i<len_t;++i){
            int c=t[j][i]-'a';
            cur=ac[cur].to[c];
            ans+=1ll*(len_t-i)*((1ll*(i+2)*ac[cur].s_num%mod-ac[cur].s_len+mod)%mod)%mod;
            ans%=mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

标签:AC,int,sum,len,2023CCPC,fail,自动机
From: https://www.cnblogs.com/dttttttt/p/17792122.html

相关文章

  • Java中logback的学习
    转:https://blog.csdn.net/lijiafa/article/details/109465399Logback常用配置介绍LOGBack简介官方手册:https://logback.qos.ch/manual/introduction.html介绍作者CekiGülcü在Java日志领域世界知名。他创造了Log4J,这个最早的Java日志框架即便在JRE内置日志功能的竞争下仍然......
  • Anaconda在Windows上安装后终端指令不生效
    查看环境变量是否已经配置好;环境变量配置好应该是"Scripts"文件夹作为conda指令的根目录而不是别的,例如,我的环境变量配置路径如下:C:\Users\ezhar\anaconda3\Scripts......
  • Anaconda环境备份
    在Windows电脑上,为了避免系统崩溃,或是为了将相同的环境拷贝到其它电脑上在装好Anaconda环境之后克隆并使用,可以将.conda文件夹全盘打个压缩包存起来,再到另一个电脑上将它们与新装的替换掉。例如,我的路径是C:\Users\ezhar\.conda,我就直接把这个文件夹压缩了一下,实现备份。......
  • https://www.modb.pro/db/1717179181560324096 --转载 Oracle 批量更新(BULK)优化技巧
    面对一个需要更新大量数据的任务,我平时的处理方法是通过循环,每N行提交来完成这个任务。这样做的两个主要原因:1、频繁地提交大量小事务比处理和提交一个大事务更快,也更高效2、没有足够的UNDO空间今天在学到了一种新的解决思路,在此记录一下方便后面使用。  假设我们有一个表T,......
  • Capture One 23:RAW图像的魔法师,开启你的摄影艺术之旅 mac/win版
    CaptureOne23,这不仅仅是一款RAW图像编辑软件,更是一款为你开启摄影艺术之旅的魔法师。这个强大的工具将带你进入RAW图像的世界,让你自由地探索并创造出令人惊艳的摄影作品。无论你是专业摄影师,还是摄影爱好者,CaptureOne23都能根据你的需求提供全面的解决方案。→→↓↓载Captur......
  • InDesign 2024:创造卓越的版面设计,让排版设计焕发光彩 mac/win版
    InDesign2024,一款专为版面设计而生的强大工具,帮助创意专业人士提升排版设计的魅力。无论是杂志、报纸、书籍,还是引人入胜的视觉展示,InDesign2024都能让你在创意的海洋中自由翱翔。→→↓↓载InDesign2024mac/win版InDesign2024拥有直观的界面和强大的排版功能,可以轻松应对......
  • 卸载oracle11g
    1.卸载1.1停止使用Oracle的服务停用oracle服务,进入计算机管理,在服务中,找到oracle开头的所有服务,右击选择停止。1.2.运行卸载Oracle数据库程序在开始菜单中找到Oracle安装产品,点击运行Oracle自带的卸载程序UniversalInstaller工具卸载。1.3.删除使用Oracle的服......
  • Mac brew安装Java8 && Mac配置多个Java版本
    安装Java8 1.打开终端,输入以下命令安装brew: `/usr/bin/ruby-e"$(curl-fsSLhttps://raw.githubusercontent.com/Homebrew/install/master/install)"` 2.安装Java8: `brewtapcaskroom/versions` `brewcaskinstalljava8` 3.验证Java8是否安装成功: `j......
  • wpf Interaction Triggers 绑定任意方法、任意Command
     framework版本引入命名空间通过在代码中引入System.Windows.Interactivity.dll,引入了这个dll后我们就能够使用这个里面的方法来将事件映射到ViewModel层了,我们来看看具体的使用步骤,第一步就是引入命名控件xmlns:i="clr-namespace:System.Windows.Interactivity;assembl......
  • 每天5分钟复习OpenStack(七)内存虚拟化
    标题中的存储虚拟化,涉及到两个方面,分别是内存和磁盘的虚拟化技术。内存的虚拟化就不得不提EPT和VPID技术.首先申明下本人其实不想写一些纯理论的东西,但是架不住面试经被问,为此特将一些特别复杂的技术底层都隐去,尽量将技术讲的简单,我个人信奉一句话'Ifyoucan'texplainits......