后缀自动机 牛客[AHOI2013]差异
时间:2019-10-25
本文章向大家介绍后缀自动机 牛客[AHOI2013]差异,主要包括后缀自动机 牛客[AHOI2013]差异使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
题目链接:https://ac.nowcoder.com/acm/problem/19894
后缀自动机推荐博客:https://www.luogu.org/blog/Kesdiael3/hou-zhui-zi-dong-ji-yang-xie
这里我们必须要先求出给出字符串中所有后缀两两之间的最长公共前缀长度之和,想用后缀自动机做,在后缀自动机上的每一个点都代表了原串中的多个唯一子串,但是这里面最长的那个子串一定是原串的一个前缀,并且这个点所代表的其他子串一定是这个前缀的后缀。所以我们可以暂且理解为SAM上的每一个点都代表了原串的一个前缀,这个点代表的其他子串这里先不管。
然后SAM上每一个点都会有一个fa指针,fa指针指向的那个节点所代表的最长子串也是原串的一个前缀,并且这个前缀同时还是当前点所代表的前缀的后缀。我们以自动机上的每一个叶子节点为起点向它的fa方向遍历,在遍历的过程中我们就可以算出所有前缀两两之间的最长公共后缀之和了。然后我们发现我们求出的东西刚好和题目需要我们求出的东西相反,于是我们把题目给出的字符串翻转一下,用这个翻转之后的字符串来构到自动机,然后题目就变成了求翻转字符串的所有前缀两两之间的最长后缀之和,然后用总数减去这个和就行了。
代码:
#include<iostream> #include<cstring> #include<algorithm> #include<queue> #include<map> #include<stack> #include<cmath> #include<vector> #include<set> #include<cstdio> #include<string> #include<deque> using namespace std; typedef long long ll; #define eps 1e-8 #define INF 0x3f3f3f3f #define maxn 2000005 struct node{ int len,fa; int ch[26]; }trie[maxn]; int last,tot; ll len; ll sum,ans; char str[maxn]; ll size[maxn]; int c[maxn],x[maxn]; void init(){ last=tot=1; memset(size,0,sizeof(size)); sum=ans=0; } void insert(int c){ int p=last; int np=last=++tot; size[tot]=1; trie[np].len=trie[p].len+1; for(;p&&trie[p].ch[c]==0;p=trie[p].fa) trie[p].ch[c]=np; if(p==0) trie[np].fa=1; else{ int q=trie[p].ch[c]; if(trie[p].len+1==trie[q].len) trie[np].fa=q; else{ int nq=++tot; trie[nq]=trie[q]; trie[nq].len=trie[p].len+1; trie[q].fa=trie[np].fa=nq; for(;p&&trie[p].ch[c]==q;p=trie[p].fa) trie[p].ch[c]=nq; } } } void cal(){ //基数排序,按照len值从小到大排序 for(int i=0;i<=tot;i++) c[i]=0; for(int i=2;i<=tot;i++) c[trie[i].len]++; for(int i=2;i<=tot;i++) c[i]+=c[i-1]; for(int i=tot;i>=2;i--) x[c[trie[i].len]--]=i; //从叶子节点开始遍历fail树 for(int i=tot;i>=2;i--){ int c=x[i]; //(当前节点代表的字符串出现的次数)*(它fail指向的字符串(是前一个字符串的后缀)的长度)*乘以2 //这里之所以要再乘以size[trie[i].fa]是因为fail指向的那个节点的size可能为0,它可能是我们在构造sam时创建用来将一个点划分为两个点的节点 sum+=2*size[c]*(trie[trie[c].fa].len)*size[trie[c].fa]; //因为trie[c].fa代表的前缀是trie[c]代表前缀的后缀,所以我们要把trie[c]出现的次数叠加到trie[c].fa上面 size[trie[c].fa]+=size[c]; } } int main() { scanf("%s",str); init(); //我们需要求的是str所有后缀两两之间的最长公共前缀长度之和,所以我们将str翻转 //这样问题就转化成为了求翻转之后字符串的所有前缀两两之间的最长公共后缀之和了 len=strlen(str); for(int i=len-1;i>=0;i--){ insert(str[i]-'a'); } cal(); ans=len*(len+1)/2*(len-1); printf("%lld\n",ans-sum); return 0; }
原文地址:https://www.cnblogs.com/6262369sss/p/11740662.html
- 人们可能会犯的7个数据错误
- java:POI导出excel
- WordPress自定义栏目运用实例III:添加原创/转载文章不同版权声明
- 另一个强大的Visualizers :Mole For Visual Studio
- WordPress自定义栏目运用实例V:为加密文章添加密码提示文字
- java基础:所有参数皆是按值参数
- 使用Hystrix提高系统可用性
- Spring Security笔记:解决CsrfFilter与Rest服务Post方式的矛盾
- GitHub新开放项目FoolNLTK:一个便捷的中文处理工具包
- hessian学习
- 制作WordPress侧边栏“博客统计”小工具并集成在主题中的方法
- Struts2、Spring MVC4 框架下的ajax统一异常处理
- 前11月网游收入1341亿元同比增超两成 你贡献了多少?
- struts2: config-browser-plugin 与 convention-plugin 学习
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- centos7 hbase1.4.13+hadoop2.7.1+单机环境搭建
- 关于kubeconfig多集群切换
- 代码版本管理规范
- 基于云原生系统的应用分发系统设计
- vue实现文字表情同时输入的方法
- 不同类型的ABAP内表读写性能比较
- JavaScript和ABAP的MD5计算源代码
- 读取SAP CRM One Order应用日志的优化方式
- 悟空活动中台 - 基于 WebP 的图片高性能加载方案
- DES ECB加解密的Java实现
- win10 uwp 不显示 SplashScreen 欢迎界面的方法
- WPF 在 XAML 写 C# 代码
- 解析 HashMap源码值概括
- 解析 HashMap 源码之基本操作
- Docker手册