[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 & ...
随机推荐
- TensorFlow中屏蔽warning的方法
问题 使用sudo pip3 install tensorflow安装完CPU版tensorflow后,运行简单的测试程序,出现如下警告: I tensorflow/core/platform/cpu ...
- Python3基础教程(十八)—— 测试
编写测试检验应用程序所有不同的功能.每一个测试集中在一个关注点上验证结果是不是期望的.定期执行测试确保应用程序按预期的工作.当测试覆盖很大的时候,通过运行测试你就有自信确保修改点和新增点不会影响应用程 ...
- Model 模型
Model 模型模型是你的数据的唯一的.权威的信息源.它包含你所存储的数据的必要字段和行为.通常,每个模型对应数据库中唯一的一张表. 每个模型都是dhango.db.models.Model 的一个P ...
- 判断请求是否为ajax
判断请求是否为ajax 转载:http://www.cnblogs.com/tony-jingzhou/archive/2012/07/30/2615612.html x-requested-with ...
- intellij idea关闭重复代码提醒
- Java中的反射--Reflect
在张孝祥老师的Java讲解中,学习到了Java反射的一部分知识,觉得有必要好好学习一下哈. 一.反射的理解 经典总结:反射就是把Java类中的各种成分映射成为相应的Java类 例如:一个Java类中用 ...
- js 将页面保存为图片
<!DOCTYPE html><html><head><title>保存为images</title><meta charset=&q ...
- [LUOGU] P2187 小Z的笔记
看范围猜方程,应该是O(n)级别的 f[i]表示前i个合法的最小代价,转移需要枚举断点位置,O(n^2) f[i]表示前i个合法留下的最大个数,同时更新距离最近的26个字母的位置,O(n)转移 f[i ...
- MySQL本地登录及数据库导入导出
注意:本地MySQL服务要开启 更新整个数据库 1.将正式服务器上的数据库做备份 登录到正式服务器,执行如下命令:(注意空格) mysqldump -uroot –p密码 数据库名 -P 接口 --d ...
- 剑指Offer(书):二进制中1的个数
题目:输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. 分析:下面这两种方法都可以,不过第二种更好一些. public int numberOf1(int n) { int count ...