上学期很认真地学了一些字符串的常用工具,各种 suffix structre,但是其实对后缀自动机这个部分是理解地不太透彻的,以致于看了师兄A这题的代码后,我完全看不懂,于是乎重新看回一些学习后缀自动机的博客和代码,重新理解了一些概念,感觉自己对于后缀自动机的理解深了一些。

先说说题意,给了三个串,问对于长度为x的串,满足(i1,i2,i3)(s1[i1..i1+k-1]==s2[i2...i2+k-1]==s3[i3+i3+k-1])有多少组,要输出全部的组数。

师兄的代码的做法是这样的,插入三个串到后缀自动机上,中间用不同的分隔符隔开。然后对于每个状态求它的right集合的大小,求的是该字符串在三个串里的right集合的大小。具体的处理方法好像是这样的,因为有了分隔符,所以如果存在到达分隔符的后继,说明该状态是当前字符串的1个后缀,初始化为1,然后就dfs一遍,将right集合加回去。 此时对于每个状态right[0]*right[1]*right[2]即是该集合表示的字符串的三元对的大小,那么这样的三元对可以更新多少呢? 由后缀自动机的知识可以知道,对于状态s,它可以更新长度为min(s),max(s)的大小,max(s)就是下面的val,而后缀自动机的性质是min(s)=max(pre)-1。所以对于每个三元对段更,由于是先更新再询问,所以可以先打上标记,然后再回加,这样就可以求出正确的答案了。下面贴一记从师兄那里学习到的代码

#pragma warning(disable:4996)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <string>
using namespace std; #define mod 1000000007
#define ll long long
#define maxn 610000 struct State{
State *suf, *go[30];
int val;;ll right[3];
State() :suf(0), val(0){
memset(go, 0, sizeof(go));
}
}*root,*last; State statePool[maxn], *cur; void init(){
cur = statePool;
root = last = cur++;
} void extend(int w){
State *p = last, *np = cur++;
np->val = p->val + 1;
while (p&&!p->go[w]) p->go[w] = np, p = p->suf;
if (!p) np->suf = root;
else{
State *q = p->go[w];
if (p->val + 1 == q->val) np->suf = q;
else{
State *nq = cur++;
memcpy(nq->go, q->go, sizeof q->go);
nq->val = p->val + 1;
nq->suf = q->suf;
q->suf = nq;
np->suf = nq;
while (p&&p->go[w] == q){
p->go[w] = nq, p = p->suf;
}
}
}
last = np;
} char str[maxn]; int n;
bool vis[maxn]; void dfs(State *u){
if (vis[u - statePool]) return;
if (!vis[u - statePool]){
vis[u - statePool] = true;
for (int i = 0; i < 3; i++){
if (u->go[26 + i]) u->right[i] = 1;
}
for (int i = 0; i < 26; i++){
if (u->go[i]) {
dfs(u->go[i]);
for (int k = 0; k < 3; k++){
u->right[k] = (u->right[k] + u->go[i]->right[k]) % mod;
}
}
}
}
} ll num[maxn]; int main()
{
init(); int n = 1e8;
scanf("%s", str);
n = min(n, int(strlen(str)));
for (int i = 0; str[i]; i++) extend(str[i] - 'a');
extend(26); scanf("%s", str);
n = min(n, int(strlen(str)));
for (int i = 0; str[i]; i++) extend(str[i] - 'a');
extend(27); scanf("%s", str);
n = min(n, int(strlen(str)));
for (int i = 0; str[i]; i++) extend(str[i] - 'a');
extend(28);
dfs(root); int tot = cur - statePool; for (int i = 1; i < tot; i++){
State *u = &statePool[i];
ll tmp = u->right[0] % mod * u->right[1] % mod*u->right[2] % mod;
num[u->val] = (num[u->val] + tmp) % mod;
num[u->suf->val] = ((num[u->suf->val] - tmp) % mod + mod) % mod;
} for (int i = tot-2; i >= 1; i--){
num[i] = (num[i] + num[i + 1]) % mod;
}
for (int i = 1; i <= n; i++){
if (i>1) printf(" ");
printf("%I64d", num[i]);
}
puts("");
}

Codeforces 452E Three Strings(后缀自动机)的更多相关文章

  1. Codeforces 235C Cyclical Quest - 后缀自动机

    Some days ago, WJMZBMR learned how to answer the query "how many times does a string x occur in ...

  2. HDU 6208 The Dominator of Strings 后缀自动机

    The Dominator of Strings Time Limit: 3000/3000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java ...

  3. Codechef2015 May - Chef and Strings (后缀自动机)

    用后缀自动机统计出出现1~n次的串的数量f[i] 对于ans[k]=sigma(f[i]*C(i,k)) i>=k ; mo=; ..maxn] of dword; nt:..maxn,'a'. ...

  4. Codeforces 452E Three strings 字符串 SAM

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF542E.html 题目传送门 - CF452E 题意 给定三个字符串 $s1,s2,s3$ ,对于所有 $L ...

  5. CF 149E Martian Strings 后缀自动机

    这里给出来一个后缀自动机的题解. 考虑对 $s$ 的正串和反串分别建后缀自动机. 对于正串的每个节点维护 $endpos$ 的最小值. 对于反串的每个节点维护 $endpos$ 的最大值. 这两个东西 ...

  6. [Codeforces 452E] Three Strings

    [题目链接] https://codeforces.com/contest/452/problem/E [算法] 构建后缀数组 用并查集合并答案即可 时间复杂度 : O(NlogN) [代码] #in ...

  7. CF 452E. Three strings(后缀数组+并查集)

    传送门 解题思路 感觉这种题都是套路之类的??首先把三个串并成一个,中间插入一些奇怪的字符,然后跑遍\(SA\).考虑按照\(height\)分组计算,就是每个\(height\)只在最高位计算一次, ...

  8. Codeforces.700E.Cool Slogans(后缀自动机 线段树合并 DP)

    题目链接 \(Description\) 给定一个字符串\(s[1]\).一个字符串序列\(s[\ ]\)满足\(s[i]\)至少在\(s[i-1]\)中出现过两次(\(i\geq 2\)).求最大的 ...

  9. 字符串(后缀自动机):Codeforces Round #129 (Div. 1) E.Little Elephant and Strings

    E. Little Elephant and Strings time limit per test 3 seconds memory limit per test 256 megabytes inp ...

随机推荐

  1. Moses创建一个翻译系统的基本过程记录,以后会按照每个过程详细说明,并给出每个步骤的参数说明

    软件需求: 首先你必须要有Moses(废话哈哈).然后要有GIZA++用作词对齐(traning-model.perl的时候会用到).IRSTLM产生语言模型 大致步骤: 大体的步骤如下: 准备Par ...

  2. OC编写使用调试器

    OC编写使用调试器 编写代码免不了,Bug.那么Debug就是程序员的必备技能了.本文和大家一起探讨,如何在应用开发编写代码过程中,使用日志项消息:以及使用动作.条件.迭代控制增强断点. 记录信息 在 ...

  3. EChart 关于图标控件的简单实用

    1.下载前段框架并放入项目中去. 2.在js中调用 <!DOCTYPE html> <html lang="en"> <head> <me ...

  4. oracle 11g 表空间使用率

    Oracle数据库表空间使用量查询: select b.file_name 物理文件名,b.tablespace_name 表空间,b.bytes/1024/1024 大小M,(b.bytes-sum ...

  5. js 与或运算符 || && 妙用(转)

    原文转自:http://www.jb51.net/article/21339.htm 首先出个题: 假设对成长速度显示规定如下: 成长速度为5显示1个箭头: 成长速度为10显示2个箭头: 成长速度为1 ...

  6. 转:CentOS 7 安装Nginx

    一.准备工作: 1.安装必备工具: ? 1 2 3 $ yum -y install gcc gcc-c++ autoconf automake   $ yum -y install zlib zli ...

  7. git简单入门

    git简单入门 标签(空格分隔): git git是作为程序员必备的技能.在这里就不去介绍版本控制和git产生的历史了. 首先看看常用的git命令: git init git add git comm ...

  8. 二、break,continue区别

    break:作用于switch,和循环语句,用于跳出,或者称为结束 break语句单独存在,下面不要定义其他语句,因为执行不到,编译会失败,当循环套时,break会跳出当前所在循环,要跳出外部循环,只 ...

  9. js节点解析

    在JS中,每一个节点都有一个nodeType 属性,用于表明节点的类型.节点类型由在Node 类型中定义的下列12个数值常量来表示,任何节点类型必居其一: Node.ELEMENT_NODE(1); ...

  10. Python字符编码详解

    1. 字符编码简介 1.1. ASCII ASCII(American Standard Code for Information Interchange),是一种单字节的编码.计算机世界里一开始只有 ...