[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 & ...
随机推荐
- Java Script 学习笔记(一)
示例如下: JavaScript-警告(alert 消息对话框) 我们在访问网站的时候,有时会突然弹出一个小窗口,上面写着一段提示信息文字.如果你不点击“确定”,就不能对网页做任何操作,这个小窗口就是 ...
- 在DLL中创建窗口时一个值得注意的地方 — UnregisterClass
背景描述: 今天要测试一份注入代码,拿以前写的创建窗口的DLL来做测试. 第一次注入时一切正常,窗口被成功创建并显示,但在第二次加载时窗口没有显示出来. 经过研究发现在第二次加载DLL时Registe ...
- Python3简明教程(六)—— 数据结构
简单的来说,数据结构(data structure)是计算机中存储.组织数据的方式.比如我们之前使用过的列表就是一种数据结构,在这里我们还会深入学习它.之前也有简单的介绍. 列表 >>&g ...
- DROP USER - 删除一个数据库用户帐号
SYNOPSIS DROP USER name DESCRIPTION 描述 DROP USER 从数据库中删除指定的用户. 它不删除数据库里此用户所有的表,视图或其他对象. 如果该用户拥有任何数据库 ...
- jquery 定位
jquery 定位 <html> <head> <title>jquery 定位</title> </head> <body> ...
- JS中的setInterval 函数体带参数f方法
1.setInterval(function code,delaytime); 在设置自动调用执行function code时,我们可以采用下面三种方式来解决. 一.采用字符串形式:(参数不能被周期性 ...
- MySQL丨03丨基本语句
MySQL语句都是以 ; 号结尾的 看库(刘大婶直接面对的是各种档案袋) show databases; 建库(新弄了一个档案袋) create database database_name; 删库( ...
- 二手GTX650
某鱼入手一二手华硕显卡GTX650 店主信誉挺高的,到手图片 我K,被骗了翻新的假显卡,华硕没有过这个样式的GTX650啊,还是新的散热风扇及前挡板,不管了先把风扇拆了,竟然连风扇散热硅脂都是刚刚涂上 ...
- Linux文件和目录的权限笔记
查看文件或者目录的权限命令:ls -al # -a 表示全部文件包含隐藏文件,-l 表示列出每个文件的详细信息 比如执行 ls -al total 115 drwxr--x--- 4 root roo ...
- 第二天,学习if,变量,注释
zz_age = 31guss_age=int(input("input your answer:"))if guss_age == zz_age: print ("Yo ...