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. Android内存泄露(全自动篇)

    写了可执行文件启动器Launcher.jar及一些批处理,通过它们就可以自动的以一定的时间间隔提取Hprof和进程的内存信息: 一.需要的库 可执行文件启动器:lib\Launcher.jar 注:关 ...

  2. 一个简单的139邮箱登录脚本--->java-selenium

    import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebE ...

  3. UVA 1479 Graph and Queries (Treap)

    题意: 给一个无向图,再给一系列操作(以下3种),输出最后的平均查询结果. (1)D X 删除第x条边. (2)Q X k  查询与点X相连的连通分量中第k大的点的权值. (3)C X v  将点X的 ...

  4. Git理论知识补充

    转自: http://www.cnblogs.com/hnrainll/archive/2012/11/13/2768003.html 对于任何一个文件,在 Git 内都只有三种状态:已提交(comm ...

  5. 测试当前C环境的栈帧增长方向以及传递参数时的压栈顺序

    前文链接:上次由于一个很常见的printf-bug(下文有提及)引发了我对栈的思考,并写下了一点总结.这次就尝试对不同的C环境进行实践,检测其传递参数的一些性质. 这是今天写的检查C环境的一段程序.能 ...

  6. 深入Docker 存储驱动 (转)

    参考: http://static.dockerone.com/ppt/filedriver.html#28

  7. run_debug和run_demo的区别

    run_demo:给一张图,直接生成测试出来的框,输入不用给gt框 run_debug:生成ap值,生成的图片既有gt框也有测试得到的结果框 run_demo的源码demo_test放在example ...

  8. Java垃圾回收之新生代垃圾收集器

    问题:什么是Stop-the-World? 1.JVM由于要执行GC而停止了应用程序的执行 2.任何一种GC算法中都会发生 3.多数GC优化通过减少Stop-the-world发生的时间来提高程序的性 ...

  9. python+Eclipse+pydev环境搭建1

    编辑器: Eclipse + pydev插件 1. Eclipse是写JAVA的IDE, 这样就可以通用了,学习代价小.  学会了Eclipse, 以后写Python或者JAVA 都可以. 2. Ec ...

  10. windows文件备份到linux:windows定时任务+cwrsync+ssh免密码认证

    一.安装cwrsync 二.创建密钥对,实现ssh免密码验证 linux服务器上 [root@zabbix ~]# ssh-keygen Generating public/private rsa k ...