折腾了好久。不过收获还是很多的。第一次自己去画SAM所建出来fail树。深入体会了这棵树的神奇性质。

当然,我最终靠着自己A掉了。(这是我第一次推SAM的性质(以前都是抄别人的,感觉自己好可耻),不过感觉好像是摸着黑行走啊!)

这道题,可以先对第一个串建出后缀自动机。然后第二个串在后缀自动机上跑。

首先,SAM所建出的fail树的性质有:

1: 树上一个节点对应了多个串,串的个数是 len[x] - len[fa[x]], 同时它们都出现了 sz[x] 次(感觉好像说不大清,可以看一下代码对于sz数组的处理)。

2: 设串的长度为 l 那么每个节点 x 的 sz[x] * (len[x] - len[fa[x]]) 的和为 (l+1) * l / 2

3:   树上一个节点的所有后代所代表的串都是以这个节点代表的串为后缀的串。

4:   根据性质3推出: 如果一个字符串匹配到一个树上的某个节点, 那么这个节点的所有祖先所代表的所有祖先都是它的子串,但那个节点本身所代表的所以串并不一定都是他的子串。

所以这道题的ans应该呼之欲出了。

(说了这么多,你们知道解题的思路是什么吗?显然对于子串的问题,我们肯定是假设当前匹配到第i个字符的时候,把以第i个字符结尾的所有字串都处理掉。)

那么假设当前匹配到 i 字符时转移到了节点x, 那么x对答案的贡献就是 祖先所代表的所有的串(可以预处理)和  匹配到 i 字符时,(第二个串与第一个串的最长匹配长度-len[fa[x]] )*sz[x]

第二个串与第一个串的最长匹配长度-len[fa[x]](这个就是第二个串在x节点内所能匹配的子串数)

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #define rep(i,j,k) for(register int i = j; i <= k; i++)
 5 #define dow(i,j,k) for(register int i = j; i >= k; i--)
 6 #define ez(i,j) for(edge*i = head[j]; i; i=i->next)
 7 #define maxn 200005
 8 #define ll long long
 9 using namespace std;
10
11 struct edge{ int to; edge*next; } e[maxn<<1], *pt = e, *head[maxn<<1];
12 inline void add(int x,int y) { pt->to = y, pt->next = head[x], head[x] = pt++; }
13
14 int last = 1, u, v, nv, p, tot = 1, sz[maxn<<1], dep[maxn<<1], fa[maxn<<1], son[maxn<<1][26];
15 inline void extend(int c) {
16     u = last; p = last = ++tot; dep[p] = dep[u] + 1; sz[p] = 1;
17     while( u && !son[u][c] ) son[u][c] = p, u = fa[u];
18     if( !u ) fa[p] = 1;
19     else {
20         v = son[u][c];
21         if( dep[v] == dep[u] + 1 ) fa[p] = v;
22         else {
23             nv = ++tot; dep[nv] = dep[u] + 1;
24             fa[nv] = fa[v]; fa[v] = fa[p] = nv;
25             memcpy(son[nv],son[v],sizeof(son[v]));
26             while( son[u][c] == v ) son[u][c] = nv, u = fa[u];
27         }
28     }
29 }
30
31 int q[maxn<<1], tong[maxn<<1];
32 inline void pre() {
33     rep(i,1,tot) tong[dep[i]]++;
34     rep(i,1,tot) tong[i] += tong[i-1];
35     dow(i,tot,1) q[tong[dep[i]]--] = i;
36     int x;
37     dow(i,tot,1) x = q[i], sz[fa[x]] += sz[x];
38
39 }
40 char c1[maxn]; ll dp[maxn<<1];
41 #define to i->to
42 inline void dfs(int x) {
43     dp[x] += 1ll * sz[x] * (dep[x] - dep[fa[x]]);
44     ez(i,x) dp[to] += dp[x], dfs(to);
45 }
46
47 int main() {
48     scanf("%s", c1); int s1 = strlen(c1);
49     rep(i,0,s1-1) extend(c1[i]-'a');
50     pre(); rep(i,1,tot) if( fa[i] ) add(fa[i],i); dfs(1);
51     //rep(i,1,tot) { rep(j,0,5) cout<<son[i][j]<<" "; cout<<endl; }
52     //rep(i,1,tot) cout<<sz[i]<<" "<<dp[i]<<" "<<dep[i]-dep[fa[i]]<<" "<<fa[i]<<endl;
53     scanf("%s", c1); s1 = strlen(c1);
54     int t, p = 1, l = 0; ll ans = 0;
55     rep(i,0,s1-1) {
56         t = c1[i] - 'a';
57         while( p && !son[p][t] ) p = fa[p];
58         if( !p ) p = 1, l = 0;
59         else l = min(l,dep[p]) + 1, p = son[p][t];
60         ans += dp[fa[p]], ans += 1ll * (l - dep[fa[p]]) * sz[p];
61     }
62     cout<<ans<<endl;
63     return 0;
64 }

转载于:https://www.cnblogs.com/83131yyl/p/5483535.html

4566: [Haoi2016]找相同字符 SAM相关推荐

  1. BZOJ 4566 [Haoi2016]找相同字符

    后缀数组 或 后缀自动机 后缀数组的做法 O(nlogn) O(nlogn) : 串接起来.要求的就是后缀数组中不属于同一串的后缀的LCP.暴力做 O(n2) O(n^2) .发现我们一直都在区间取m ...

  2. [后缀自动机][树形DP] BZOJ 4566: [Haoi2016]找相同字符

    Solution Solution 又看了半天的SAM,感觉这次看陈老师的讲稿流畅多了.. 这道题的话,对 A A串建自动机,BB串在上面匹配. B B匹配到状态uu贡献就是 |rightu|∗(le ...

  3. [bzoj4566][HAOI2016]找相同字符

    4566: [Haoi2016]找相同字符 Time Limit: 20 Sec Memory Limit: 256 MB Submit: 113 Solved: 64 [Submit][Status ...

  4. BZOJ4566: [Haoi2016]找相同字符

    BZOJ4566: [Haoi2016]找相同字符 Description 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数. 两个方案不同当且仅当这两个子串中有一个位置不同 ...

  5. [洛谷P3181] [HAOI2016]找相同字符

    洛谷题目链接:[HAOI2016]找相同字符 题目描述 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 输入输出格式 输入 ...

  6. 【bzoj4566】[Haoi2016]找相同字符【后缀自动机】

    题目传送门 题解:在文本串上建后缀自动机,用模式串在后缀自动机上跑.扫一遍模式串,在后缀自动机上走,走不了就跳fail再走. 走的过程中,维护模式串与文本串匹配的最大长度,并且统计答案. 怎么统计答案 ...

  7. [HAOI2016]找相同字符

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

  8. BZOJ4566: [Haoi2016]找相同字符(后缀自动机)

    题意 题目链接 Sol 直接在SAM上乱搞 枚举前缀,用SAM统计可以匹配的后缀,具体在匹配的时候维护和当前节点能匹配的最大值 然后再把parent树上的点的贡献也统计上,这部分可以爆跳parent树 ...

  9. [bzoj4566][HAOI2016]找相同字符(后缀数组)

    题目 传送门 题解 这里:把两个串用一个很大的字符连接起来,求一个后缀数组. 考虑怎样暴力的算答案. 在 rank  r a n k rank数组中从前往后枚举起点,对于每个枚举的起点,都暴力的往后扫 ...

最新文章

  1. 阿里AI摘图像识别竞赛WebVision桂冠,万物识别准确率创世界纪录
  2. [转]oracle 存储过程的基本语法 及注意事项
  3. 如何让Sublime Text编辑器支持新的ABAP关键字
  4. SAAS,象B2C那样的B2B分销系统
  5. openshift_云上的播放框架变得简单:Openshift模块
  6. 重要的,是那些训练中被多次遗忘的样本
  7. php正则循环,PHP正则解析多重循环模板示例
  8. Cacti监控Varnish
  9. 考研高等数学张宇30讲笔记——第七讲 零点问题与微分不等式
  10. Effective minidump
  11. CSS中常用的选择器
  12. 基于51单片机控制的BUCK开关电源Proteus仿真
  13. 微信小程序统一服务消息接口
  14. 干预型ASO手段——积分墙
  15. 拓展题 系列I之科普系列
  16. 【Linux实验】用户和组群账户管理
  17. 富途牛牛api_k牛刮毛是改善API的好方法
  18. Android 接入穿山甲激励视频广告步骤与错误总结
  19. 在Ubuntu 18下安装SIMULIA Abaqus 2020
  20. 2021年广东省安全员A证第三批(主要负责人)考试总结及广东省安全员A证第三批(主要负责人)试题及解析

热门文章

  1. OpenCV+python:Canny边缘检测算法
  2. python中不同进制的整数之间可以直接运算_Python 进制转换、位运算
  3. php和css一样吗,php和css一样吗
  4. 换了路由器电脑都连不上网了_技术丨电脑连不上网,我要如何冲浪?
  5. php solr 更新数据类型,Solr更新文档数据
  6. linux系统分析命令,Linux操作系统基础解析之(四)——Linux基本命令剖析(2)
  7. java最全人名数组_java 里有两个方法 第一个方法定义一个数组 每个数组都是一个学生类 每个学生有姓名 学号 年龄...
  8. Python,OpenCV应用轮廓逼近算法,检测对象的形状
  9. 利用统计滤波方法去除空中漂浮物 以及去噪
  10. vs中.exe运行闪退的解决办法