[Bzoj4566][Haoi2016]找相同字符(广义后缀自动机)
4566: [Haoi2016]找相同字符
Time Limit: 20 Sec Memory Limit: 256 MB
Submit: 861 Solved: 495
[Submit][Status][Discuss]
Description
Input
两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母
Output
输出一个整数表示答案
Sample Input
aabb
bbaa
Sample Output
HINT
题解:
第一眼看题,跟子串有关不是Sam就是Sa (雾)
第二眼看题,不就是个广义后缀自动机,邪魅一笑的发现这题很水啊
先来看看如何用sam求一个串的不同子串
建好sam后,一个结点所代表的子串是根节点到它的各个路径,然后路径数量刚好是dis[i] -dis[fa[i]];
设sam里有dt个结点,则一个串的不同子串为∑(dis[i] - dis[fa[i]]), i ∈[1,dt]
根据sam的特点,一个点出现了,那么必然它的父亲也出现了,因为它的父亲是它的后缀。
那么对于每一次sam出来的点,我们使它权值w[i]++,然后按dis基数排序后,倒着扫一遍w[fa[i]] += w[i];
那么对于每个子串,假设它对应结点为i,那么它出现次数为 w[i]
这样我们就利用sam的性质对于一个串求出了它不同子串数量,或者某个子串出现次数。
再来看看广义后缀自动机。看看这篇博文http://blog.csdn.net/wangzhen_yu/article/details/45481269
看不懂没关系,简单解释就是按一个串建完sam后,后面的每个串的last回到根节点,又继续建,不清空之前串的记录。具体看代码
然后对于每个点,我们就可以发现,它可以代表第一个字符串子串,也可以代表第二个字符串子串。
所以我们按一个串的方法,求出两个字符串对应的w数组,那么每个点i就有w[i][0] 和 w[i][1];
这个点所代表的dis[fa[i]] - dis[i]个在第一个串的子串都出现了w[i][0] 次
这个点所代表的dis[fa[i]] - dis[i]个在第二个串的子串都出现了w[i][1] 次
那么这个点所产生的贡献就是 (dis[fa[i]] - dis[i]) * w[i][0] * w[i][1];
最后就发现答案是 ∑ (dis[fa[i]] - dis[i]) * w[i][0] * w[i][1] , i ∈[1,dt]
需要注意的是因为用的是基数排序,相同dis的点相对先后顺序是不变的,所以如果一次sam加了两个点,要使代表当前last的点出现位置靠后,具体见代码
AC代码:
# include <cstdio>
# include <cstring>
# include <iostream>
using namespace std;
typedef long long LL;
const int N = << ;
int ch[N][],fa[N],dis[N],dt = ,que[N],l,ls,bac[N];
char str[N];LL ans,sz[N][];
int Sam(int last,int c)
{
int u = last,np;
while(u && !ch[u][c])que[++que[]] = u,u = fa[u];
if(!u)
{
np = ++dt;dis[np] = dis[last] + ;fa[np] = ;
while(que[])ch[que[que[]--]][c] = np;
}
else
{
int v = ch[u][c];
if(dis[v] == dis[u] + )
{
np = ++dt;dis[np] = dis[last] + ;fa[np] = v;
while(que[])ch[que[que[]--]][c] = np;
}
else
{
int av = ++dt;np = ++dt;
dis[np] = dis[last] + ;
while(que[])ch[que[que[]--]][c] = np;
dis[av] = dis[u] + ;
memcpy(ch[av],ch[v],sizeof ch[v]);
fa[av] = fa[v];fa[v] = fa[np] = av;
while(u && ch[u][c] == v)ch[u][c] = av,u = fa[u];
}
}
return np;
}
int main()
{
scanf("%s",str);l = strlen(str);ls = ;
for(int i = ;i < l;i++)ls = Sam(ls,str[i] - 'a'),sz[ls][]++;
scanf("%s",str);l = strlen(str);ls = ;
for(int i = ;i < l;i++)ls = Sam(ls,str[i] - 'a'),sz[ls][]++;
for(int i = ;i <= dt;i++)bac[dis[i]]++;
for(int i = ;i < N;i++)bac[i] += bac[i - ];
for(int i = dt;i >= ;i--)que[--bac[dis[i]]] = i;
for(int i = dt;i >= ;i--)sz[fa[que[i]]][] += sz[que[i]][],sz[fa[que[i]]][] += sz[que[i]][];
for(int i = ;i <= dt;i++)ans += 1LL * (dis[i] - dis[fa[i]]) * (sz[i][] * sz[i][]);
printf("%lld\n",ans);
}
[Bzoj4566][Haoi2016]找相同字符(广义后缀自动机)的更多相关文章
- [HAOI2016]找相同字符 广义后缀自动机_统计出现次数
题目描述:给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 输入输出格式输入格式:两行,两个字符串 s1,s2,长度分别为n ...
- BZOJ 4566 [Haoi2016]找相同字符 ——广义后缀自动机
建立广义后缀自动机. 然后统计子树中的siz,需要分开统计 然后对(l[i]-l[fa[i]])*siz[i][0]*siz[i][1]求和即可. #include <cstdio> #i ...
- bzoj 4566 [Haoi2016]找相同字符——广义后缀自动机
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4566 每个后缀结尾处 ct[ ] = 1 ,按拓扑序 dp 一下就能求出 right 集合的 ...
- BZOJ4566 HAOI2016找相同字符(后缀自动机)
对第一个串建SAM,第二个串在上面跑,记录当前前缀匹配的最长后缀长度l,每次考虑当前前缀的贡献,对于当前所在节点显然是|right|*(l-len[fa]),而对于其parent树上所有祖先的贡献显然 ...
- BZOJ4566&&lg3181 HAOI找相同字符(广义后缀自动机)
BZOJ4566&&lg3181 HAOI找相同字符(广义后缀自动机) 题面 自己找去 HINT 给定两个文本串,问从两个串中各取一个非空子串,使这俩子串相同,问方案有多少种.我的思路 ...
- BZOJ_4566_[Haoi2016]找相同字符_后缀自动机
BZOJ_4566_[Haoi2016]找相同字符_后缀自动机 Description 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两 个子串中有 ...
- 【BZOJ4566】找相同字符(后缀自动机)
[BZOJ4566]找相同字符(后缀自动机) 题面 BZOJ 题解 看到多串处理,\(SA\)就连起来 \(SAM???\) 单串建自动机 然后其他串匹配 对于一个串建完\(SAM\)后 另一个串在\ ...
- bzoj 4566 找相同字符 —— 广义后缀自动机
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4566 建出两个串的广义后缀自动机: 统计每个点在两个串中出现次数的子树和,其实就是在两个串中 ...
- 【BZOJ4566】找相同字符【后缀自动机】
题意 给定两个字符串,求两个字符串相同子串的方案数. 分析 那么将字符串s1建SAM,然后对于s2的每个前缀,都在SAM中找出来,并且计数就行. 我一开始的做法是,建一个u和len,顺着s2跑SAM, ...
- BZOJ4566 [Haoi2016]找相同字符 【后缀数组】
题目 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两 个子串中有一个位置不同. 输入格式 两行,两个字符串s1,s2,长度分别为n1,n2.1 & ...
随机推荐
- 应用-如何使不同的企业使用独自的数据源。使用ejb3.0+jboss6.2EAP+JPA
摘要: 如何使不同的企业使用独自的数据源.使用ejb3.0+jboss6.2EAP+JPA10C应用系统被多个企业同时使用,为了提供个性化服务,如何使不同的企业使用独自的 ...
- 关于 propertychange 兼容性问题
on 事件 $('body').on('property input','.class',function(){ alert(123); });
- android 代码中及xml中设置透明
在布局文件的属性中,比如要设置一个LineaerLayout的背景为灰色透明.首先查RGB颜色表灰色是:#9E9E9E,AA代表透明,(透明度从00到FF,00表示完全透明),所以,设置其属性:and ...
- AS400服务程序总结
1.服务程序的创建和调用过程 1.1生成module 1.2编写BND文件确定输出接口 1.3生成服务程序 1.3.运行调用程序时,将服务程序导入到作业内存区active group,常驻内存 2.结 ...
- js数字转金额,ajax调用接口,后台返回html(完整页面),打开新窗口并写入html
一.转换成金额形式 function toMoney(num){ if(num){ if(isNaN(num)) { alert("金额中含有不能识别的字符"); return; ...
- Navicat 模型生成表
打开模型 -> 左上角文件 -> 导出SQL 打开sql文件,将sql在数据库执行,注意主键递增.日期类型 根据当前时间戳更新是否需要(默认选中的)等等
- JS常用字符串处理方法应用总结
这篇文章主要总结了JS常用字符串的处理方法,需要的朋友可以参考下 1.indexOf()方法,从前往后查找字符串位置,大小写敏感,从0开始计数.同理,lastIndexOf() 方法从后往前,两个 ...
- MySQL存储过程实践
对employees数据库建立存储过程 创建不含有输入输出变量的存储过程 DELIMITER // -- 设定语句结束分隔符 DROP PROCEDURE IF EXISTS GetEmployees ...
- PHP中的预定义常量、类常量和魔术常量的区别
PHP 向它运行的任何脚本提供了大量的预定义常量.不过很多常量都是由不同的扩展库定义的,只有在加载了这些扩展库时才会出现,或者动态加载后,或者在编译时已经包括进去了. 对于一些基本的常量是这些常量在 ...
- vue项目中设置跨域
config->index.js 'use strict' // Template version: 1.3.1 // see http://vuejs-templates.github.io/ ...