当前位置: 首页 > news >正文

后缀自动机(SAM)

后缀自动机

P3804 【模板】后缀自动机(SAM)

可解决的问题:

  1. 在另一个字符串中搜索一个字符串的所有出现位置;

  2. 计算给定的字符串中有多少个不同的子串.

算法步骤:

先假设我们建好了这个结构,考虑添加一个字符 \(c\)

考虑三种情况:

情况一:原字符串 \(s\) 的后缀路径上,没有经由 \(c\) 的转移,直接连一条 \(c\) 边。

情况二:原字符串 \(s\) 的后缀路径上已经有了 \(c\) 边但只连了一个 \(c\) 边,直接把 \(cur\)\(link\) 指向该点。

情况三:原字符串 \(s\) 的后缀路径上已经有了 \(c\) 边但还有其他边,新建一个点 \(clone\) 克隆该点,并把 \(len\) 改为仅从 \(p\)\(c\) 这一个字符转移过来,即 \(sam[clone].len=sam[p].len+1\)。并把在 \(p\) 链上通过 \(c\) 指向 \(q\) 的所有节点都指向 \(clone\)。最后修改当前节点和 \(q\) 点的 \(link\)

代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+10;
struct node{int len,link;//分别记录endpos集合相同的子串最长长度和前向边指向的节点(后缀链接)map<int,int> ch;//记录转移点
}sam[N];
int idx,last;
ll val[N],ans;//dfs后的val[i]表示有多少个子串在i处结尾
vector<int> q[N];
void insert(int c){int cur=++idx;//新节点 sam[cur].len=sam[last].len+1;//长度等于上一个+1val[cur]=1;int p=last;while(p!=-1&&sam[p].ch.count(c)==0){//先连没有c这条边的sam[p].ch[c]=cur;p=sam[p].link;}if(p==-1) sam[cur].link=0;//找到头了else{int q=sam[p].ch[c];//q是已经连了c这条边的集合if(sam[q].len==sam[p].len+1) sam[cur].link=q;//如果长度说明只连了一个 c 就直接把新节点的前向边连过来 else{//否则说明p还连了其他边 int clone=++idx;sam[clone]=sam[q];//克隆q节点 sam[clone].len=sam[p].len+1;//改变它的len为只连c的len sam[cur].link=sam[q].link=clone;//因为clone的len比q的小,所以clone是q的前缀,因此cur和q的link都是clone while(p!=-1&&sam[p].ch[c]==q){//现在q就要作为一个不在这条链上的节点了,把这条链上原本走c连到q的点全部连到clone,这样q就可以彻底脱离这条链了 sam[p].ch[c]=clone;p=sam[p].link;}}	}last=cur;//更新last 
}
void dfs(int x){//计算以x为结尾的字串的总数 for(int i=0;i<q[x].size();i++){int y=q[x][i];dfs(y);val[x]+=val[y];}
}
int main(){string s;cin>>s;sam[0].link=-1;//重要!!!方便判断是否走到头,不加会死循环 for(int i=0;i<s.size();i++) insert(s[i]-'a');for(int i=1;i<=idx;i++) q[sam[i].link].push_back(i);//通过前向边 link 建图 dfs(0);for(int i=1;i<=idx;i++) if(val[i]>1) ans=max(ans,val[i]*sam[i].len);cout<<ans;return 0;
} 
http://www.jsqmd.com/news/518111/

相关文章:

  • 《如何高效提升提示系统可靠性与效率?提示工程架构师有话说》
  • 嵌入式C多核性能天花板突破实录(仅限芯片原厂FAE内部文档解密):绕过CMSIS标准库,直驱GICv3中断分发器实现核间唤醒延迟<83ns
  • web后端----oatpp临时笔记
  • Ant Download Manager Pro v2.16.8 蚂蚁下载器便携版 高速下载神器
  • 北京上门收酒,高端洋酒路易十三回收,京城亚南酒业专业上门 - 品牌排行榜单
  • 吐血推荐! AI论文软件 千笔ai写作 VS 万方智搜AI,开源免费首选!
  • 计算机毕业设计:Python基于协同过滤的在线图书销售与推荐系统 Django框架 可视化 协同过滤推荐算法 机器学习 大数据 大模型(建议收藏)✅
  • 【RV1106】基于SPI驱动ST7735S屏幕,移植LVGL实现图片显示全流程解析
  • 北京上门收酒,地方老酒回收,京城亚南酒业不挑款,诚信全收 - 品牌排行榜单
  • 2026冲刺用!10个AI论文网站深度测评:论文写作全流程必备工具推荐
  • 2026化妆学校排行|零基础必看!避坑不踩雷,择校少走3年弯路 - 品牌测评鉴赏家
  • GPTK进阶指南:除了装游戏,这些Wine Prefix的维护技巧让你少走弯路
  • 2026年值得关注的化妆培训学校,新手必看 - 品牌测评鉴赏家
  • 手把手教你用2SK184搭建JFET共源放大电路(附Multisim仿真文件)
  • 鸿蒙分布式软总线:RPC协议如何重塑跨设备通信体验
  • 看完就会:开源免费AI论文软件,千笔写作工具 VS 灵感ai!
  • STM32调试神器Event Recorder:告别串口打印,5分钟搞定高效Debug(基于CubeMX)
  • 探索ANSYS-Simpack的柔性化处理
  • 别再让程序动不动就崩溃了!Python异常处理,你该这么玩!
  • 电机参数辨识就像给电机做CT扫描,不拆机就能摸清内部脾气。咱们今天直接上干货,撸起袖子从大厂实战代码里找门道
  • django《Python程序设计》课程智能问答系统 智能AI客服问答系统
  • STM32F10x标准库工程搭建避坑指南:从固件库下载到LED点亮全流程
  • GLM-OCR赋能Dify.AI:为低代码平台添加视觉理解能力
  • STC8G1K08A单片机ADC读取避坑指南:电位器模块连接与串口打印实战
  • 基于博途1200PLC + HMI水塔水位控制系统仿真探索
  • 地热井耐高温液位计源头生产厂家推荐 - WHSENSORS
  • 基于105报文DSC功能,实现博能传动伺服双轴高精度绝对同步
  • 手把手教你用Java搞定那个俄文论坛的注册验证码(ASCII八进制解码实战)
  • 讲讲2026年绍兴荷花苗芦苇苗一站式采购加工厂,排名前十有哪些 - myqiye
  • 光伏MPPT算法仿真:开启初学者的探索之旅