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]找相同字符(广义后缀自动机)的更多相关文章

  1. [HAOI2016]找相同字符 广义后缀自动机_统计出现次数

    题目描述:给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 输入输出格式输入格式:两行,两个字符串 s1,s2,长度分别为n ...

  2. BZOJ 4566 [Haoi2016]找相同字符 ——广义后缀自动机

    建立广义后缀自动机. 然后统计子树中的siz,需要分开统计 然后对(l[i]-l[fa[i]])*siz[i][0]*siz[i][1]求和即可. #include <cstdio> #i ...

  3. bzoj 4566 [Haoi2016]找相同字符——广义后缀自动机

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4566 每个后缀结尾处 ct[ ] = 1 ,按拓扑序 dp 一下就能求出 right 集合的 ...

  4. BZOJ4566 HAOI2016找相同字符(后缀自动机)

    对第一个串建SAM,第二个串在上面跑,记录当前前缀匹配的最长后缀长度l,每次考虑当前前缀的贡献,对于当前所在节点显然是|right|*(l-len[fa]),而对于其parent树上所有祖先的贡献显然 ...

  5. BZOJ4566&&lg3181 HAOI找相同字符(广义后缀自动机)

    BZOJ4566&&lg3181 HAOI找相同字符(广义后缀自动机) 题面 自己找去 HINT 给定两个文本串,问从两个串中各取一个非空子串,使这俩子串相同,问方案有多少种.我的思路 ...

  6. BZOJ_4566_[Haoi2016]找相同字符_后缀自动机

    BZOJ_4566_[Haoi2016]找相同字符_后缀自动机 Description 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两 个子串中有 ...

  7. 【BZOJ4566】找相同字符(后缀自动机)

    [BZOJ4566]找相同字符(后缀自动机) 题面 BZOJ 题解 看到多串处理,\(SA\)就连起来 \(SAM???\) 单串建自动机 然后其他串匹配 对于一个串建完\(SAM\)后 另一个串在\ ...

  8. bzoj 4566 找相同字符 —— 广义后缀自动机

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4566 建出两个串的广义后缀自动机: 统计每个点在两个串中出现次数的子树和,其实就是在两个串中 ...

  9. 【BZOJ4566】找相同字符【后缀自动机】

    题意 给定两个字符串,求两个字符串相同子串的方案数. 分析 那么将字符串s1建SAM,然后对于s2的每个前缀,都在SAM中找出来,并且计数就行. 我一开始的做法是,建一个u和len,顺着s2跑SAM, ...

  10. BZOJ4566 [Haoi2016]找相同字符 【后缀数组】

    题目 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两 个子串中有一个位置不同. 输入格式 两行,两个字符串s1,s2,长度分别为n1,n2.1 & ...

随机推荐

  1. 大型Java Web项目的架构和部署问题

    一位ID是jackson1225的网友在javaeye询问了一个大型Web系统的架构和部署选型问题,希望能提高现有的基于Java的Web应用的服务能力.由于架构模式和部署调优一直是Java社区的热门话 ...

  2. react基础语法(二)常用语法如:样式 ,自定义属性,常用表达式

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  3. 洛谷 P2153 [SDOI2009]晨跑

    题目描述 Elaxia最近迷恋上了空手道,他为自己设定了一套健身计划,比如俯卧撑.仰卧起坐等 等,不过到目前为止,他坚持下来的只有晨跑. 现在给出一张学校附近的地图,这张地图中包含N个十字路口和M条街 ...

  4. PowerBI 应用时间智能(生成日期表)

    简介 Power BI Desktop -是一款由微软发布的自助式商业智能工具,功能强大.易于使用.其中还可以通过微软云连多个数据源并且使用数据源来创建可视化表盘. 但是几乎所有的BI都需要展示如何随 ...

  5. QT 学习笔记概述

    以下笔记为在看书和实践的过程中的部分记录总结: 0. 窗口布局 1) 支持绝对布局和布局管理器布局; 2) 绝对布局不够灵活.无法自动调整大小,需要手动编写代码调整: 3) 布局管理器管理布局比较灵活 ...

  6. vscode vue template 下 style 的样式自动提示 #bug 这个搞完vue语法esLint就又不好使了,ERR

    网上都是 "*.vue": "vue",改成"*.vue": "html" 就ok了   "files.ass ...

  7. 课外作业(建立double类型的小数,按照四舍五入保留2位小数)

    举例:

  8. 对faster rcnn代码讲解的很好的一个

    http://www.cnblogs.com/houkai/p/6824455.html http://blog.csdn.net/u014696921/article/details/6032142 ...

  9. 简单批处理命令直接启动你的AVD

    大家都知道,要想启动AVD,一般方法是先打开Android SDK and AVDmanager,再选择你要启动的AVD选择start(废话) 那么,有没有一种简单的方法在任何位置一键启动你指定的av ...

  10. 纯css实现同一页面下选择之后更换内容效果

    实现效果为如下:在同一页面下,当我选中输入手机号时,出现手机号输入框,当我选中输入验证码时,出现验证码输入框,当我选中设置密码时,出现密码框 在这里有一个小技巧,就是  1.对下面的输入框设置同样的样 ...