【spoj8222-Substrings】sam求子串出现次数
http://acm.hust.edu.cn/vjudge/problem/28005
题意:给一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值。求F(1)..F(Length(S)) 。
题解:
关键问题在于统计某个串出现了多少次。
在后缀自动机中,答案即为包含了这个串的状态的right集合的大小。
后缀自动机有两张DAG,一张是trans图,一张是parent树
从trans图的角度出发,right集合的大小为该状态走到结束状态的方案数
从parent树的角度出发,parent树是反串的后缀树,right集合的大小为该状态的子树中有多少个结点代表了反串的一个后缀(也就是原串的前缀)
我们采取第二种计数方法,先将包含原串前缀的状态的right集合设为1(相当于在反串的后缀树中将后缀结点标记为1)
因为parent树中我们并没有把儿子记下来,所以没办法直接对parent树bfs,但我们知道儿子的len严格大于父亲的len,所以我们按len的长度进行排序,然后按len从大到小,用当前点更新parent的答案
我们发现一个结点代表的长度是一个区间,我们先只考虑该结点代表的最长长度,然后我们再用长串去更新短串(因为一个长串出现k次,它的所有后缀都至少出现k次)即可————引用自http://blog.csdn.net/hbhcy98/article/details/51055733
首先要求节点x表示的最长的子串,也就是长度为step[x]的这个串出现了多少次——该节点的right集合。
right集合到底怎么求?
从parent树的角度出发,parent树是反串的后缀树,right集合的大小为该状态的子树中有多少个结点代表了反串的一个后缀(也就是原串的前缀)
在建好的自动机上跑一遍原串,经过的节点r[x]=1;
然后找出自动机的拓扑序,按着拓扑序的逆序for一遍,更新pre[x]。
一个节点贡献的子串长度区间是[min[x],max[x]],我们开始只考虑了该节点代表的最大长度max[x],也就是长度为step[x]这个子串。
然后我们用长串去更新短串。
因为sam是在线的,从root开始跳到x节点的路径必定是主链上root到x这条最长串(原串的前缀)的后缀。
一个长串出现k次,它的所有后缀都至少出现k次。
f[i-1]=maxx(f[i-1],f[i]);
机智啊。。。。。。我看了好几个题解才看懂。。。。TAT。。。
- #include<cstdio>
- #include<cstdlib>
- #include<cstring>
- #include<iostream>
- #include<queue>
- #include<ctime>
- using namespace std;
- const int N=*;
- char s[N];
- int tot,last,sl,cl;
- int son[N][],pre[N],step[N],in[N],c[N],r[N],f[N];
- bool vis[N];
- queue<int> Q;
- int maxx(int x,int y){return x>y ? x:y;}
- int add_node(int x){step[++tot]=x;/*r[tot]=1;*/return tot;}
- void clear()
- {
- memset(r,,sizeof(r));
- memset(son,,sizeof(son));
- memset(pre,,sizeof(pre));
- memset(step,,sizeof(step));
- tot=;add_node();last=;
- }
- void extend(int ch)
- {
- int p=last,np=add_node(step[p]+);
- while(p && !son[p][ch]) son[p][ch]=np,in[np]++,p=pre[p];
- if(!p) pre[np]=;
- else
- {
- int q=son[p][ch];
- if(step[q]==step[p]+) pre[np]=q;
- else
- {
- int nq=add_node(step[p]+);
- memcpy(son[nq],son[q],sizeof(son[q]));
- for(int i=;i<=;i++)
- if(son[q][i]) in[son[q][i]]++;
- pre[nq]=pre[q];
- pre[np]=pre[q]=nq;
- while(son[p][ch]==q) son[p][ch]=nq,in[nq]++,in[q]--,p=pre[p];
- }
- }
- last=np;
- }
- void find_tp()
- {
- while(!Q.empty()) Q.pop();
- memset(vis,,sizeof(vis));
- Q.push();vis[]=;cl=;
- while(!Q.empty())
- {
- int x=Q.front();vis[x]=;c[++cl]=x;Q.pop();
- for(int i=;i<=;i++)
- {
- int y=son[x][i];
- if(!y) continue;
- in[y]--;
- if(!in[y] && !vis[y]) vis[y]=,Q.push(y);
- }
- }
- }
- int main()
- {
- freopen("a.in","r",stdin);
- scanf("%s",s+);
- sl=strlen(s+);
- clear();
- for(int i=;i<=sl;i++) extend(s[i]-'a'+);
- int x=,ch;
- for(int i=;i<=sl;i++)
- {
- ch=s[i]-'a'+;
- x=son[x][ch];
- r[x]++;
- }
- find_tp();
- for(int i=cl;i>=;i--)
- {
- x=c[i];
- r[pre[x]]+=r[x];
- // printf("r %d = %d step = %d\n",x,r[x],step[x]);
- f[step[x]]=maxx(f[step[x]],r[x]);
- }
- // for(int i=1;i<=sl;i++) printf("%d ",f[i]);printf("\n");
- for(int i=sl;i>=;i--) f[i-]=maxx(f[i],f[i-]);
- for(int i=;i<=sl;i++) printf("%d\n",f[i]);
- return ;
- }
【spoj8222-Substrings】sam求子串出现次数的更多相关文章
- HDU 1686 Oulipo【kmp求子串出现的次数】
The French author Georges Perec (1936–1982) once wrote a book, La disparition, without the letter 'e ...
- str2int HDU - 4436 后缀自动机求子串信息
题意: 给出 n 个串,求出这 n 个串所有子串代表的数字的和. 题解; 首先可以把这些串构建后缀自动机(sam.last=1就好了), 因为后缀自动机上从 root走到的任意节点都是一个子串,所有可 ...
- H - Repeats (重复最多子串的次数)
题目链接:https://cn.vjudge.net/contest/283743#problem/H 题目大意:T组数据,给你一个字符串,然后让你求这个字符串的重复最多子串的次数. 具体思路:论文题 ...
- hiho#1445 重复旋律5 求子串数量 后缀自动机
题目传送门 题意:给出一个字符串,求子串的个数. 思路:后缀自动机的题真是每做一题就更理解一些. SAM中的每一状态$p$都代表了一种子串,而p包含的字符串的个数是$len[p]-len[fa[p]] ...
- poj 3461 Oulipo(kmp统计子串出现次数)
题意:统计子串出现在主串中的次数 思路:典型kmp #include<iostream> #include<stdio.h> #include<string.h> ...
- 求子串-KPM模式匹配-NFA/DFA
求子串 数据结构中对串的5种最小操作子集:串赋值,串比较,求串长,串连接,求子串,其他操作均可在该子集上实现 数据结构中串的模式匹配 KPM模式匹配算法 基本的模式匹配算法 //求字串subStrin ...
- 串的模式匹配算法(求子串位置的定位函数Index(S,T,pos))
串的模式匹配的一般方法如算法4.5(在bo4-1.cpp 中)所示:由主串S 的第pos 个字 符起,检验是否存在子串T.首先令i 等于 pos(i 为S 中当前待比较字符的位序),j 等于 1(j ...
- [SPOJ8222]Substrings
[SPOJ8222]Substrings 试题描述 You are given a string S which consists of 250000 lowercase latin letters ...
- hihocoder-1419 后缀数组四·重复旋律4 求连续重复次数最多的子串
对于重复次数,如果确定了重复子串的长度len,那重复次数k=lcp(start,start+len)/len+1.而暴力枚举start和len的复杂度是O(n^2),不能接受.而有一个规律,若我们只枚 ...
随机推荐
- 使用TFS需要注意的地方
1. 用管理员添加了本地映射,然后用其他用户就添加不了映射,一定要先用管理员账户去把映射 删除掉: 2. 在正式使用TFS时,一定需要在VS工具的设置里面,设置一下,签出时自动获取最新的代码.(默认是 ...
- 事务消息中心-TMC
此文已由作者杨凯明授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 背景 为什么要做事务消息中心 原有kqueue的方式缺点: 降低业务库性能 占用业务库磁盘 历史数据管理成本 ...
- 【性能监控】虚拟内存监控命令vmstat详解
一.Vmstat说明 vmstat是Virtual Meomory Statistics(虚拟内存统计)的缩写,可对操作系统的虚拟内存.进程.CPU活动进行监控.vmstat 工具提供了一种低开销的系 ...
- python中socket、socketio、flask-socketio、WebSocket的区别与联系
socket.socketio.flask-socketio.WebSocket的区别与联系 socket 是通信的基础,并不是一个协议,Socket是应用层与TCP/IP协议族通信的中间软件抽象层, ...
- CSS3 : transform 与 transform-origin 属性可以使元素样式发生转变
CSS3 : transform 用于元素样式的转变,比如使元素发生位移.角度变化.拉伸缩小.按指定角度歪斜 transform结合transition可实现各类动画效果 transform : tr ...
- python3学习之路_day1
登录程序1.输入用户名密码2.认证成功后显示欢迎信息3.输错三次后锁定 #!/usr/bin/env python #_*_coding:utf-8_*_ #by anthor gushiren 20 ...
- LeetCode 410——分割数组的最大值
1. 题目 2. 解答 此题目为 今日头条 2018 AI Camp 5 月 26 日在线笔试编程题第二道--最小分割分数. class Solution { public: // 若分割数组的最大值 ...
- f3d源码解读
Fomo3D 源码解析, 部署指南 https://www.meiwen.com.cn/subject/efntbftx.html 原文链接 Fomo3D 合约源码分析 准备工作 环境准备 (用于调试 ...
- windows2008 R2 系统 安装wampserver提示“缺少msvcr110.dll文件”处理办法
windows2008 R2 系统 安装wampserver提示“缺少msvcr110.dll文件”处理办法 原因分析: 因缺少Visual C++ Redistributable for Visua ...
- phpquery中文手册
[简介] phpQuery是一个基于PHP的服务端开源项目,它可以让PHP开发人员轻松处理DOM文档内容.更有意思的是,它采用了jQuery的思想,使得可以像使用jQuery一样处理页面内容,获取想要 ...