三种做法:BZOJ 2780: [Spoj]8093 Sevenk Love Oimaster
@
题意
链接:here
有\(n\)个大串\(s\)和\(m\)个询问,每次给出一个字符串\(t\)询问在多少个大串中出现过。
\(1\le n\le 10000,1\le m\le 60000,\sum|s|\le 100000,\sum |t|\le 360000\)
思路
初步分析
- 对\(n\)个串建广义后缀自动机(每个串插入子前将当前节点归为到根节点即可)。
- 询问即把询问串\(t\)一步步在后缀自动机上走即可,走到节点\(p\),若\(p\)为\(0\),则答案为\(0\)。
- 反之,答案即为后缀连接树上\(p\)节点子树内不同颜色数。
- 上述颜色数解释为:将\(n\)个串设为\(n\)种颜色,将串\(i\)包含的所有节点添加\(i\)这种颜色,一个节点可能包含很多颜色。
法1:广义后缀自动机+玄学的暴力?
- 看不懂这样写的复杂度,跑得飞快?有人说是\(O(n\sqrt n)\),但是跑得比\(O(nlog(n))\)和\(O(n)\)还快。。
- 对\(n\)个串跑一边广义后缀自动机。
- 对后缀自动机里的每个节点记录一下上一个经过他的是哪一个字符串以及有多少字符串经过这个字符串。
- 可是这个要咋维护呢?在线\(or\)离线?
- 玄学做法:每插入一个节点后,沿着他的后缀连接向根节点暴力跑,更新贡献,遇到节点\(p\)上一次被经过的串也是这个串时,则\(break\)。
法2:广义后缀自动机+set启发式合并
- 每个节点用\(set\)储存经过他的字符串种类,然后把后缀连接树建出来,自叶子节点向上\(set\)启发式合并即可。
- 我感觉复杂度应该比\(nlog(n)\)要大一点吧,也不会超过\(nlog(n)^2\)。
法3:广义后缀自动机+dfs序+离线树状数组
- 这就是一个套路做法了,很常见的离线树状数组应用吧?
- 继续初步分析的讨论,跑一遍\(dfs\)序后查询子树问题就变成了查询序列的一段区间类似问题了。
- 那就把所有询问离线下来,按查询右端点排序。
- 把树按\(dfs\)序变成一个序列,从头开始遍历,树状数组更新颜色数量,为保证一种颜色的贡献只计算一次,记录一下每种颜色上一次更新的位置,这里更加,那里减即可。(这个套路可以看我博客里一些树状数组题解即可。
- 当已处理过得元素不少于询问的右端点时,即开始查询答案。
- 感觉很常见啊。。
嘤嘤嘤
AC_Code1
#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define fi first
#define se second
#define endl '\n'
#define o2(x) (x)*(x)
#define BASE_MAX 30
#define mk make_pair
#define eb emplace_back
#define all(x) (x).begin(), (x).end()
#define clr(a, b) memset((a),(b),sizeof((a)))
#define iis std::ios::sync_with_stdio(false); cin.tie(0)
#define my_unique(x) sort(all(x)),x.erase(unique(all(x)),x.end())
using namespace std;
#pragma optimize("-O3")
typedef long long LL;
typedef pair<int, int> pii;
inline LL read() {
LL x = 0;int f = 0;
char ch = getchar();
while (ch < '0' || ch > '9') f |= (ch == '-'), ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x = f ? -x : x;
}
inline void write(LL x) {
if (x == 0) {putchar('0'), putchar('\n');return;}
if (x < 0) {putchar('-');x = -x;}
static char s[23];
int l = 0;
while (x != 0)s[l++] = x % 10 + 48, x /= 10;
while (l)putchar(s[--l]);
putchar('\n');
}
int lowbit(int x) { return x & (-x); }
template<class T>T big(const T &a1, const T &a2) { return a1 > a2 ? a1 : a2; }
template<typename T, typename ...R>T big(const T &f, const R &...r) { return big(f, big(r...)); }
template<class T>T sml(const T &a1, const T &a2) { return a1 < a2 ? a1 : a2; }
template<typename T, typename ...R>T sml(const T &f, const R &...r) { return sml(f, sml(r...)); }
void debug_out() { cerr << '\n'; }
template<typename T, typename ...R>void debug_out(const T &f, const R &...r) {cerr << f << " ";debug_out(r...);}
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]: ", debug_out(__VA_ARGS__);
#define print(x) write(x);
const LL INFLL = 0x3f3f3f3f3f3f3f3fLL;
const int HMOD[] = {1000000009, 1004535809};
const LL BASE[] = {1572872831, 1971536491};
const int mod = 998244353;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int MXN = 5e5 + 5;
const int MXE = 5e6 + 6;
int n, m;
char s[MXN];
int sum[MXN], pre[MXN];
struct Suffix_Automaton {
static const int maxn = 5e5 + 105;
//basic
// map<char,int> nex[maxn * 2];
int nex[maxn*2][26];
int link[maxn * 2], len[maxn * 2];
int last, cnt;
LL tot_c;//不同串的个数
void clear() {
tot_c = 0;
last = cnt = 1;
link[1] = len[1] = 0;
// nex[1].clear();
memset(nex[1], 0, sizeof(nex[1]));
}
void add(int c, int id) {
int p = last;
int np = ++cnt;
// nex[cnt].clear();
memset(nex[cnt], 0, sizeof(nex[cnt]));
len[np] = len[p] + 1;
last = np;
while (p && !nex[p][c])nex[p][c] = np, p = link[p];
if (!p)link[np] = 1, tot_c += len[np] - len[link[np]];
else {
int q = nex[p][c];
if (len[q] == len[p] + 1)link[np] = q, tot_c += len[np] - len[link[np]];
else {
int nq = ++cnt;
len[nq] = len[p] + 1;
// nex[nq] = nex[q];
memcpy(nex[nq], nex[q], sizeof(nex[q]));
link[nq] = link[q];
link[np] = link[q] = nq;
sum[nq] = sum[q] , pre[nq] = pre[q];
tot_c += len[np] - len[link[np]];
while (nex[p][c] == q)nex[p][c] = nq, p = link[p];
}
}
for(p = np; p && pre[p] != id; p = link[p]) pre[p] = id, ++ sum[p];
}
} sam;
int main() {
#ifndef ONLINE_JUDGE
freopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
//freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
// int Tim = read();
n = read(), m = read();
sam.clear();
for(int i = 1; i <= n; ++i) {
sam.last = 1;
scanf("%s", s);
int ln = strlen(s);
for(int j = 0; j < ln; ++j) sam.add(s[j] - 'a', i);
}
while(m --) {
scanf("%s", s);
int u = 1, ln = strlen(s);
for(int i = 0; i < ln; ++i) u = sam.nex[u][s[i] - 'a'];
printf("%d\n", sum[u]);
}
#ifndef ONLINE_JUDGE
cout << "time cost:" << 1.0*clock()/CLOCKS_PER_SEC << "ms" << endl;
#endif
return 0;
}
AC_Code2
const int MXN = 2e5 + 5;
const int MXE = 4e5 + 6;
int n, m;
char s[MXE];
int sum[MXN], pre[MXN];
set<int> orz[MXN];
vector<int> mp[MXN];
struct Suffix_Automaton {
static const int maxn = 2e5 + 105;
//basic
// map<char,int> nex[maxn * 2];
int nex[maxn*2][26];
int link[maxn * 2], len[maxn * 2];
int last, cnt;
LL tot_c;//不同串的个数
void clear() {
tot_c = 0;
last = cnt = 1;
link[1] = len[1] = 0;
// nex[1].clear();
memset(nex[1], 0, sizeof(nex[1]));
}
void add(int c, int id) {
int p = last;
int np = ++cnt;
// nex[cnt].clear();
memset(nex[cnt], 0, sizeof(nex[cnt]));
len[np] = len[p] + 1;
last = np;
orz[np].insert(id);
while (p && !nex[p][c])nex[p][c] = np, p = link[p];
if (!p)link[np] = 1, tot_c += len[np] - len[link[np]];
else {
int q = nex[p][c];
if (len[q] == len[p] + 1)link[np] = q, tot_c += len[np] - len[link[np]];
else {
int nq = ++cnt;
len[nq] = len[p] + 1;
// nex[nq] = nex[q];
memcpy(nex[nq], nex[q], sizeof(nex[q]));
link[nq] = link[q];
link[np] = link[q] = nq;
tot_c += len[np] - len[link[np]];
while (nex[p][c] == q)nex[p][c] = nq, p = link[p];
}
}
// for(p = np; p && pre[p] != id; p = link[p]) pre[p] = id, ++ sum[p];
}
} sam;
set<int>::iterator sit;
void dfs(int u) {
for(int i = 0, v; i < (int)mp[u].size(); ++ i) {
v = mp[u][i];
dfs(v);
if((int)orz[v].size() > (int)orz[u].size()) swap(orz[u], orz[v]);
for(sit = orz[v].begin(); sit != orz[v].end(); ++ sit) orz[u].insert(*sit);
}
sum[u] = (int)orz[u].size();
}
int main() {
#ifndef ONLINE_JUDGE
freopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
//freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
// int Tim = read();
n = read(), m = read();
sam.clear();
for(int i = 1; i <= n; ++i) {
sam.last = 1;
scanf("%s", s);
int ln = strlen(s);
for(int j = 0; j < ln; ++j) sam.add(s[j] - 'a', i);
}
for(int i = 2; i <= sam.cnt; ++i) mp[sam.link[i]].push_back(i);
dfs(1);
while(m --) {
scanf("%s", s);
int u = 1, ln = strlen(s);
for(int i = 0; i < ln; ++i) u = sam.nex[u][s[i] - 'a'];
printf("%d\n", sum[u]);
}
#ifndef ONLINE_JUDGE
cout << "time cost:" << 1.0*clock()/CLOCKS_PER_SEC << "ms" << endl;
#endif
return 0;
}
AC_Code3
const int MXN = 4e5 + 5;
const int MXE = 4e5 + 6;
int n, m;
char s[MXE];
int sum[MXN], pre[MXN], bit[MXN];
vector<int> mp[MXN], orz[MXN];
int did[MXN], rid[MXN], lid[MXN], inde;
void bit_add(int x, int v, int N) {
while(x <= N) {
bit[x] += v;
x += lowbit(x);
}
}
int bit_query(int x) {
if(x < 0) return 0;
int res = 0;
while(x) {
res += bit[x];
x -= lowbit(x);
}
return res;
}
struct Suffix_Automaton {
static const int maxn = 2e5 + 105;
//basic
// map<char,int> nex[maxn * 2];
int nex[maxn*2][26];
int link[maxn * 2], len[maxn * 2];
int last, cnt;
void clear() {
last = cnt = 1;
link[1] = len[1] = 0;
// nex[1].clear();
memset(nex[1], 0, sizeof(nex[1]));
}
void add(int c, int id) {
int p = last;
int np = ++ cnt;
// nex[cnt].clear();
memset(nex[cnt], 0, sizeof(nex[cnt]));
len[np] = len[p] + 1;
last = np;
orz[np].push_back(id);
while (p && !nex[p][c]) nex[p][c] = np, p = link[p];
if (!p) link[np] = 1;
else {
int q = nex[p][c];
if (len[q] == len[p] + 1) link[np] = q;
else {
int nq = ++cnt;
len[nq] = len[p] + 1;
// nex[nq] = nex[q];
memcpy(nex[nq], nex[q], sizeof(nex[q]));
link[nq] = link[q];
link[np] = link[q] = nq;
while (nex[p][c] == q)nex[p][c] = nq, p = link[p];
}
}
// for(p = np; p && pre[p] != id; p = link[p]) pre[p] = id, ++ sum[p];
}
} sam;
struct lp {
int l, r, id;
}cw[MXN];
void dfs(int u) {
did[u] = ++ inde, rid[inde] = u;
for(int i = 0, v; i < (int)mp[u].size(); ++ i) {
v = mp[u][i];
dfs(v);
}
lid[u] = inde;
}
bool cmp(const lp&a,const lp&b) {
if(a.r != b.r) return a.r < b.r; return a.l < b.l;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
//freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
// int Tim = read();
n = read(), m = read();
sam.clear();
for(int i = 1; i <= n; ++i) {
sam.last = 1;
scanf("%s", s);
int ln = strlen(s);
for(int j = 0; j < ln; ++j) sam.add(s[j] - 'a', i);
}
for(int i = 2; i <= sam.cnt; ++i) mp[sam.link[i]].push_back(i);
dfs(1);
for(int i = 1; i <= m; ++i) {
scanf("%s", s);
int u = 1, ln = strlen(s);
for(int j = 0; j < ln; ++j) u = sam.nex[u][s[j] - 'a'];
cw[i].id = i, cw[i].l = did[u], cw[i].r = lid[u];
if(u == 1) cw[i].l = cw[i].r = -1, sum[i] = 0;
// debug(cw[i].l, cw[i].r)
}
sort(cw + 1, cw + 1 + m, cmp/*[](const lp&a,const lp&b){ if(a.r != b.r) return a.r < b.r; return a.l < b.l;}*/);
int j = 1;
while(cw[j].l == -1 && j <= m) ++ j;
for(int i = 2; i <= inde; ++i) {
for(int j = 0, x; j < (int)orz[rid[i]].size(); ++j) {
x = orz[rid[i]][j];
bit_add(i, 1, sam.cnt);
if(pre[x]) bit_add(pre[x], -1, sam.cnt);
pre[x] = i;
}
while(j <= m && cw[j].r <= i) {
sum[cw[j].id] = bit_query(cw[j].r) - bit_query(cw[j].l - 1);
++ j;
}
}
for(int i = 1; i <= m; ++i) printf("%d\n", sum[i]);
#ifndef ONLINE_JUDGE
cout << "time cost:" << 1.0*clock()/CLOCKS_PER_SEC << "ms" << endl;
#endif
return 0;
}
参考
/*
* http://wyfcyx.is-programmer.com/posts/76391.html
* https://www.wandouip.com/t5i167692/
* http://www.voidcn.com/article/p-kbhgnkio-oh.html
* https://eolv.farbox.com/post/oi_2015/bzoj2780
* http://blog.miskcoo.com/2015/05/bzoj-2780
* https://www.cnblogs.com/CQzhangyu/p/7088404.html
*/
bzoj 2780: [Spoj]8093 Sevenk Love Oimaster
三种做法:BZOJ 2780: [Spoj]8093 Sevenk Love Oimaster的更多相关文章
- BZOJ 2780: [Spoj]8093 Sevenk Love Oimaster( 后缀数组 + 二分 + RMQ + 树状数组 )
全部串起来做SA, 在按字典序排序的后缀中, 包含每个询问串必定是1段连续的区间, 对每个询问串s二分+RMQ求出包含s的区间. 然后就是求区间的不同的数的个数(经典问题), sort queries ...
- BZOJ 2780: [Spoj]8093 Sevenk Love Oimaster [广义后缀自动机]
JZPGYZ - Sevenk Love Oimaster Oimaster and sevenk love each other. But recently,sevenk hea ...
- bzoj 2780 [Spoj]8093 Sevenk Love Oimaster
LINK:Sevenk Love Oimaster 询问一个模式串在多少个文本串中出现过. 考虑广义SAM 统计这种数量问题一般有三种做法. 一种 暴力bitset 这道题可能可以过? 一种 暴力跳p ...
- 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 2780 [Spoj]8093 Sevenk Love Oimaster
Description Oimaster and sevenk love each other. But recently,sevenk heard that a girl named ChuYuXu ...
- bzoj 2780: [Spoj]8093 Sevenk Love Oimaster(广义SAM)
题目大意:给出n个原串,再给出m个查询串.求每个查询串出现在了多少原串中. 题解 直接对原串建一个广义SAM,然后把每一个原串放到SAM上跑一跑,记录一下每一个状态属于多少个原串,用$size$表示. ...
- BZOJ 2780 [Spoj]8093 Sevenk Love Oimaster ——广义后缀自动机
给定n个串m个询问,问每个串在n个串多少个串中出现了. 构建广义后缀自动机,(就是把所有字符串的后缀自动机合并起来)其实只需要add的时候注意一下就可以了. 然后对于每一个串,跑一边匹配,到达了now ...
- bzoj 2780: [Spoj]8093 Sevenk Love Oimaster【广义SAM】
AC自动机比较简单,把询问串做成AC自动机然后模板串边跑变更新即可 SAM是把模板串做成广义SAM,然后每个节点存有几个模板串经过,具体方法是每次更新暴力向上跳直到有时间戳我不会证为什么时间复杂度是对 ...
- 【BZOJ2780】[Spoj]8093 Sevenk Love Oimaster 广义后缀自动机
[BZOJ2780][Spoj]8093 Sevenk Love Oimaster Description Oimaster and sevenk love each other. But r ...
随机推荐
- CSS基础知识复习
1. CSS优先级 标签内部属性 style定义的CSS > 文档内定义的css > 引用外部CSS文件 2. CSS选择器类型 . 标签选择器 . 类选择器(使用.做标识) . ID选择 ...
- vue.js样式绑定
vue.js样式绑定 class 与 style 是 HTML 元素的属性,用于设置元素的样式,我们可以用 v-bind 来设置样式属性. Vue.js v-bind 在处理 class 和 styl ...
- appium定位学习
前面也介绍过appium的一些定位方法,今天看到一篇博客,里面的方法总结的,就转载过来. 本文转自:https://www.cnblogs.com/Mushishi_xu/p/7685966.html ...
- 在阿里云 Ubuntu上通过nginx+uwsgi服务器部署Django出现的502错误
https://blog.csdn.net/luojie140/article/details/76919471 https://blog.csdn.net/sinat_21302587/articl ...
- 测开之路二十八:Flask基础之静态资源
Flask默认的存放静态资源的目录名为static 在工程下创建一个文件夹(与脚本同级) 如果想命名为其他名字,则在声明app的时候要初始化,如: 准备一张图片放在static下,返回的内容加上img ...
- 怒转一波,此人整理的Flink特别好
Apache Flink:特性.概念.组件栈.架构及原理分析 Apache Flink是一个面向分布式数据流处理和批量数据处理的开源计算平台,它能够基于同一个Flink运行时(Flink Runtim ...
- 50-python基础-python3-列表-函数sorted() 对列表进行临时排序
sorted()函数对列表进行临时排序,返回排序后的列表: 区别列表方法sort()原地修改,无返回值. 1-要保留列表元素原来的排列顺序,同时以特定的顺序呈现它们,可使用函数sorted() . 2 ...
- 委托的异步编程和同步编程的使用( Invoke 和BeginInvoke)
一,区别: 使用Invoke完成一个委托方法的封送,就类似于使用SendMessage方法来给界面线程发送消息,是一个同步方法.也就是说在Invoke封送的方法被执行完毕前,Invoke方法不会返回, ...
- go语言从例子开始之Example29.关闭通道
关闭 一个通道意味着不能再向这个通道发送值了.这个特性可以用来给这个通道的接收方传达工作已经完成的信息. Example: package main import "fmt" // ...
- GCC -l选项:手动添加链接库
链接器把多个二进制的目标文件(object file)链接成一个单独的可执行文件.在链接过程中,它必须把符号(变量名.函数名等一些列标识符)用对应的数据的内存地址(变量地址.函数地址等)替代,以完成程 ...