BZOJ 3277 串 & BZOJ 3473 字符串 (广义后缀自动机、时间复杂度分析、启发式合并、线段树合并、主席树)
标签那么长是因为做法太多了。。。
题目链接: (bzoj 3277) https://www.lydsy.com/JudgeOnline/problem.php?id=3277
(bzoj 3473) https://www.lydsy.com/JudgeOnline/problem.php?id=3473
题解:
先讲三个做法公共部分: 建出广义SAM,然后对于每个点求出它在多少字符串中出现过。
做法一
把每个字符串在广义SAM上暴力跑。每跑到一个点就暴力沿着fail树往上跳,标记跳过的点,直到跳到已标记的点为止(每个串要换用不同的标记)。
时间复杂度\(O(L\sqrt L)\) (\(n\)为串的个数,\(L\)为总长度)
写一下时间复杂度分析: (我自己想的,很有可能是错的,有错恳请大佬指出!!感谢)
假设某个字符串长度为\(x\), 则最坏情况下它一直在往深处走,并且每一步都没有碰到已经跳过的点,这种情况下其走的步数是\(\sum^{x}_{i=1}i=O(x^2)\).
但是它还要受到另一个限制,就是走的步数不超过SAM总大小\(O(L)\). 因此其对时间复杂度贡献为\(O(\min(x^2),L)\).
计算最坏情况下的时间复杂度,也就是已知\(\sum^{m}_{i=1} x_i=L\), 求\(\sum^{m}_{i=1} \min(x_i^2,L)\)的最大值。显然当\(x_i>\sqrt L\)时是没有任何意义的(白白浪费代价,价值不增加),所以就是已知\(\sum^{m}_{i=1} x_i=L\)且对于任意\(i\)有\(i\le \sqrt L\), 求\(\sum^{m}_{i=1} x_i^2\)的最大值。由函数的凹凸性知显然(或者也可以用偏导数解释,如果你愿意的话。。。)所有串长均为\(\sqrt L\)时目标函数最大,为\(O(L\sqrt L)\).
做法二
类似于BZOJ2754/BZOJ2780, 就是个数颜色问题,每个点开个set记录经过这个点的所有串,然后沿着Parent树自下而上启发式合并。时间复杂度\(O(n\log^2n)\).
线段树合并貌似可以做到\(O(n\log n)\)?
做法三
依然是数颜色,可以使用DFS序+主席树等各种神奇做法解决。时间复杂度\(O(n\log^2n)\) (?)
代码
做法一
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<vector>
#define llong long long
using namespace std;
const int N = 4e5;
const int S = 26;
struct SAM
{
int id[N+3];
int fa[N+3];
int son[N+3][S+3];
int len[N+3];
int sz[N+3];
int buc[N+3];
int oid[N+3];
int cnt[N+3];
int cid[N+3];
int mx[N+3];
int siz,rtn,lstpos;
void init() {id[0] = siz = rtn = lstpos = 1;}
int insertstr(char ch)
{
int p = lstpos,np; siz++; np = lstpos = siz; len[np] = len[p]+1; sz[np] = 1;
for(; p && son[p][ch]==0; p=fa[p]) {son[p][ch] = np;}
if(p==0) fa[np] = rtn;
else
{
int q = son[p][ch];
if(len[q]==len[p]+1) {fa[np] = q;}
else
{
siz++; int nq = siz; len[nq] = len[p]+1;
memcpy(son[nq],son[q],sizeof(son[q]));
fa[nq] = fa[q]; fa[np] = fa[q] = nq;
for(; p && son[p][ch]==q; p=fa[p]) {son[p][ch] = nq;}
}
}
return np;
}
void getsort()
{
for(int i=1; i<=siz; i++) buc[len[i]]++;
for(int i=1; i<=siz; i++) buc[i] += buc[i-1];
for(int i=siz; i>=1; i--) oid[buc[len[i]]--] = i;
}
} sam;
vector<char> str[N+3];
char a[N+3];
int n,m;
int main()
{
scanf("%d%d",&n,&m);
sam.init();
for(int i=1; i<=n; i++)
{
scanf("%s",a+1);
sam.lstpos = 1;
int lena = strlen(a+1);
for(int j=1; j<=lena; j++) {sam.insertstr(a[j]-96); str[i].push_back(a[j]);}
}
// for(int i=1; i<=sam.siz; i++) for(int j=1; j<=S; j++) if(sam.son[i][j]) printf("trans%d %d %d\n",i,j,sam.son[i][j]);
// for(int i=1; i<=sam.siz; i++) printf("i%d len%d fa%d\n",i,sam.len[i],sam.fa[i]);
sam.getsort();
for(int i=1; i<=n; i++)
{
int pos = sam.rtn;
for(int j=0; j<str[i].size(); j++)
{
pos = sam.son[pos][str[i][j]-96];
int tmp = pos;
for(; tmp && sam.cid[tmp]!=i; tmp=sam.fa[tmp]) {sam.cnt[tmp]++; sam.cid[tmp] = i;}
}
}
sam.cnt[1] = 0;
for(int i=1; i<=sam.siz; i++)
{
int u = sam.oid[i];
sam.mx[u] = sam.mx[sam.fa[u]]+(sam.cnt[u]>=m ? sam.len[u]-sam.len[sam.fa[u]] : 0);
// printf("%d mx%d\n",u,sam.mx[u]);
}
for(int i=1; i<=n; i++)
{
int pos = sam.rtn; llong ans = 0ll;
for(int j=0; j<str[i].size(); j++)
{
pos = sam.son[pos][str[i][j]-96];
ans += (llong)sam.mx[pos];
// putchar(str[i][j]); printf("pos%d\n",pos); puts("");
}
printf("%lld ",ans);
}
return 0;
}
BZOJ 3277 串 & BZOJ 3473 字符串 (广义后缀自动机、时间复杂度分析、启发式合并、线段树合并、主席树)的更多相关文章
- BZOJ 3473: 字符串 [广义后缀自动机]
3473: 字符串 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 354 Solved: 160[Submit][Status][Discuss] ...
- BZOJ 3473 字符串 ——广义后缀自动机
这题就比较有趣了. 首先匹配一遍,然后统计子树叶子节点中包含大于等于k的节点个数(HH的项链) 然后就可以搞了. 关于合法的情况数,显然是l[i]-l[fa[i]],然后向下下传即可(YY一下). # ...
- BZOJ.2780.[SPOJ8093]Sevenk Love Oimaster(广义后缀自动机)
题目链接 \(Description\) 给定n个模式串,多次询问一个串在多少个模式串中出现过.(字符集为26个小写字母) \(Solution\) 对每个询问串进行匹配最终会达到一个节点,我们需要得 ...
- BZOJ5137&&lg4081(广义后缀自动机,set启发式合并)
BZOJ5137&&lg4081(广义后缀自动机,set启发式合并) 题面 自己找去 HINT 给定多个文本串,让你查询每个文本串中有多少个本质不同的子串且这个子串只出现在当前这个文本 ...
- BZOJ2780(广义后缀自动机,set启发式合并)
BZOJ2780(广义后缀自动机,set启发式合并) 题面 自己找去 HINT 就是给多个文本串,然后每次查询的时候问你这个串在多少个文本串中出现过.因为多个文本串,那么直接就往广义后缀自动机上思考啊 ...
- bzoj 3277 串 && bzoj 3473 字符串 && bzoj 2780 [Spoj]8093 Sevenk Love Oimaster——广义后缀自动机
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3277 https://www.lydsy.com/JudgeOnline/problem.p ...
- bzoj 3277: 串 & bzoj 3473: 字符串【后缀自动机||后缀数组】
建一个广义后缀自动机(每加完一个串都返回root),在parent树上dpsum记录合法长度,打着时间戳往上跳,最后每个串在自动机上跑一变统计答案即可. 后缀数组理解起来可能方便一点,但是难写,就只说 ...
- 【bzoj3277/bzoj3473】串/字符串 广义后缀自动机
题目描述 字符串是oi界常考的问题.现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串(注意包括本身). 输入 第一行两个整数n,k.接下来n行每行一个 ...
- BZOJ 4566 [Haoi2016]找相同字符 ——广义后缀自动机
建立广义后缀自动机. 然后统计子树中的siz,需要分开统计 然后对(l[i]-l[fa[i]])*siz[i][0]*siz[i][1]求和即可. #include <cstdio> #i ...
随机推荐
- 基于CentOS7.5的 Rsync 服务详解
第1章 Rsync概述 1.1 Rsync基本概述 rsync是一款开源的备份工具,可以在不同服务器(主机)之间进行同步备份, 可实现完全备份与增量备份,因此非常适合用于架构集中式备份或异地备份等应用 ...
- Unity资源的查找
Object.Destroy static function Destroy(obj: Object, t: float = 0.0F): void; Description Removes a ...
- bzoj 1574: [Usaco2009 Jan]地震损坏Damage【dfs】
和March的那道不一样,只是非常单纯的带着贪心的dfs 首先一个点被隔断,与它相邻的所有点也会被隔断,打上删除标记,从1dfs即可 #include<iostream> #include ...
- 3-5 编程练习:jQuery实现简单的图片对应展示效果
3-5 编程练习:jQuery实现简单的图片对应展示效果 通过这个章节的学习, 老师带领大家完成了一个基本的图片切换特效,接下来,我们也实现一个类似的效果,点击相应的按钮,切换对应的图片. 效果图 : ...
- Android之条形码、二维码扫描框架(非原创)
文章大纲 一.条形码.二维码扫描框架介绍二.条形码.二维码的区别和组成结构介绍三.条形码.二维码扫描框架应用场景四.BGAQRCode-Android框架实战五.项目源码下载六.参考文章 一.条形码. ...
- 51nod1298 圆与三角形
1298 圆与三角形 题目来源: HackerRank 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 给出圆的圆心和半径,以及三角形的三个顶点,问圆同三 ...
- 257 Binary Tree Paths 二叉树的所有路径
给定一个二叉树,返回从根节点到叶节点的所有路径.例如,给定以下二叉树: 1 / \2 3 \ 5所有根到叶路径是:["1->2->5", " ...
- 数据库恢复挂起解决办法【MSSQL】
新建查询输入如下代码运行 - -把test改成你需要修复的数据库名 USE master GO ALTER DATABASE test SET SINGLE_USER GO ALTER DATABAS ...
- hibernate一对多查询
一对多查询 1,同时添加老师和学生案例 在进行具有关联关系的对象同时添加时 首先绑定对像间的关系 ---将多方关联一方 ---将一方关联多方 然后全部添加 备注: 1,保存老师对象时, 由于设置了学生 ...
- springboot与dubbo整合遇到的坑
整合环境: dubbo 2.6.2 springboot 2.1.5 遇到的问题:服务一直无法注册到zookeeper注册中心 项目结构: 使用application.properties文件: 配置 ...