BZOJ 2780: [Spoj]8093 Sevenk Love Oimaster( 后缀数组 + 二分 + RMQ + 树状数组 )

全部串起来做SA, 在按字典序排序的后缀中, 包含每个询问串必定是1段连续的区间, 对每个询问串s二分+RMQ求出包含s的区间. 然后就是求区间的不同的数的个数(经典问题), sort queries + BIT 就行了.时间复杂度O(N log N). 速度垫底了QAQ 你们都会SAM。。。。

----------------------------------------------------------------------

#include<cmath>

#include<cstdio>

#include<cctype>

#include<cstring>

#include<algorithm>

using namespace std;

#define b(i) (1 << (i))

const int maxL = 540009;

const int maxQ = 60009;

char S[maxL], str[maxL];

int N, n, q, Id[maxL], qL[maxQ], qR[maxQ], L[maxQ], R[maxQ];

int Rank[maxL], Height[maxL], Sa[maxL], cnt[maxL];

int RMQ[20][maxL], r[maxQ], ans[maxQ];

void Build() {

int m = ‘z‘ + 1, *x = Rank, *y = Height;

for(int i = 0; i < m; i++) cnt[i] = 0;

for(int i = 0; i < N; i++) cnt[x[i] = S[i]]++;

for(int i = 1; i < m; i++) cnt[i] += cnt[i - 1];

for(int i = N; i--; ) Sa[--cnt[x[i]]] = i;

for(int k = 1, p = 0; k <= N; k <<= 1, p = 0) {

for(int i = N - k; i < N; i++) y[p++] = i;

for(int i = 0; i < N; i++)

if(Sa[i] >= k) y[p++] = Sa[i] - k;

for(int i = 0; i < m; i++) cnt[i] = 0;

for(int i = 0; i < N; i++) cnt[x[y[i]]]++;

for(int i = 1; i < m; i++) cnt[i] += cnt[i - 1];

for(int i = N; i--; ) Sa[--cnt[x[y[i]]]] = y[i];

swap(x, y);

x[Sa[0]] = 0;

p = 1;

for(int i = 1; i < N; i++) {

if(y[Sa[i]] != y[Sa[i - 1]] || y[Sa[i] + k] != y[Sa[i - 1] + k]) p++;

x[Sa[i]] = p - 1;

}

if((m = p) >= N) break;

}

for(int i = 0; i < N; i++) Rank[Sa[i]] = i;

Height[0] = Height[N] = 0;

for(int i = 0, h = 0; i < N; i++) if(Rank[i]) {

if(h) h--;

while(S[i + h] == S[Sa[Rank[i] - 1] + h]) h++;

Height[Rank[i]] = h;

}

}

void Init_RMQ() {

for(int i = 0; i < N; i++)

RMQ[0][i] = Height[i];

for(int i = 1; b(i) <= N; i++)

for(int j = 0; j + b(i) <= N; j++)

RMQ[i][j] = min(RMQ[i - 1][j], RMQ[i - 1][j + b(i - 1)]);

}

inline int LCP(int l, int r) {

int t = log2(r - l + 1);

return min(RMQ[t][l], RMQ[t][r - b(t) + 1]);

}

void calc(int &L, int &R, int p, int len) {

int _l, _r;

p = Rank[p];

if(Height[p] >= len) {

_l = 0, _r = p - 1;

while(_l <= _r) {

int m = (_l + _r) >> 1;

if(LCP(m + 1, p) >= len)

L = m, _r = m - 1;

else

_l = m + 1;

}

} else

L = p;

if(Height[p + 1] >= len) {

_l = p + 1, _r = N - 1;

while(_l <= _r) {

int m = (_l + _r) >> 1;

if(LCP(p + 1, m) >= len)

R = m, _l = m + 1;

else

_r = m - 1;

}

} else

R = p;

}

struct Link {

int p;

Link* n;

} pool[maxL], *pt = pool, *H[maxL];

inline void AddL(int v, int p) {

pt->p = p, pt->n = H[v], H[v] = pt++;

}

int B[maxL];

inline void Modify(int p, int v) {

if(!p) return;

for(; p <= N; p += p & -p) B[p] += v;

}

inline int Sum(int p) {

int ret = 0;

for(; p; p -= p & -p) ret += B[p];

return ret;

}

inline bool Cmp(const int &l, const int &r) {

return qL[l] < qL[r];

}

void Work() {

Build();

Init_RMQ();

memset(B, 0, sizeof B);

for(int i = N; i--; )

if(Id[Sa[i]] >= 0) AddL(Id[Sa[i]], i);

for(int i = 0; i < n; i++)

Modify(H[i]->p + 1, 1);

for(int i = 0; i < q; i++)

calc(qL[r[i] = i], qR[i], L[i], R[i] - L[i]);

sort(r, r + q, Cmp);

int c = 0;

for(int i = 0; i < N; i++) {

while(qL[r[c]] == i) {

ans[r[c]] = Sum(qR[r[c]] + 1) - Sum(qL[r[c]]);

if(++c >= q) break;

}

if(c >= q) break;

Modify(i + 1, -1);

if(H[Id[Sa[i]]]) {

H[Id[Sa[i]]] = H[Id[Sa[i]]]->n;

if(H[Id[Sa[i]]])

Modify(H[Id[Sa[i]]]->p + 1, 1);

}

}

for(int i = 0; i < q; i++)

printf("%d\n", ans[i]);

}

inline int getstr() {

char c = getchar();

for(; !islower(c); c = getchar());

int len = 0;

for(; islower(c); c = getchar())

str[len++] = c;

return len;

}

void Init() {

scanf("%d%d", &n, &q);

N = 0;

int len;

for(int i = 0; i < n; i++) {

len = getstr();

for(int j = 0; j < len; j++) {

Id[N] = i;

S[N++] = str[j];

}

Id[N] = -1;

S[N++] = ‘$‘;

}

for(int i = 0; i < q; i++) {

len = getstr();

L[i] = N;

for(int j = 0; j < len; j++) {

Id[N] = -1;

S[N++] = str[j];

}

R[i] = N;

Id[N] = -1;

S[N++] = ‘$‘;

}

S[N - 1] = 0;

}

int main() {

Init();

Work();

return 0;

}

----------------------------------------------------------------------

2780: [Spoj]8093 Sevenk Love Oimaster

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 581  Solved: 188
[Submit][Status][Discuss]

Description

Oimaster and sevenk love each other.

    But recently,sevenk heard that a girl named ChuYuXun was dating with oimaster.As a woman‘s nature, sevenk felt angry and began to check oimaster‘s online talk with ChuYuXun.    Oimaster talked with ChuYuXun n times, and each online talk actually is a string.Sevenk asks q questions like this,    "how many strings in oimaster‘s online talk contain this string as their substrings?"

Input

There are two integers in the first line, 
the number of strings n and the number of questions q.
And n lines follow, each of them is a string describing oimaster‘s online talk. 
And q lines follow, each of them is a question.
n<=10000, q<=60000 
the total length of n strings<=100000, 
the total length of q question strings<=360000

Output

For each question, output the answer in one line.

Sample Input

3 3
abcabcabc
aaa
aafe
abc
a
ca

Sample Output

1
3
1

HINT

Source

时间: 2024-08-02 10:52:53

BZOJ 2780: [Spoj]8093 Sevenk Love Oimaster( 后缀数组 + 二分 + RMQ + 树状数组 )的相关文章

BZOJ 2780 SPOJ 8093 Sevenk Love Oimaster 后缀自动机+fenwick

题目大意:给出一些字符串,给出一些询问,每次问当前串在源串中的几个中出现过. 思路:将所有源串建立广义后缀自动机.每次新的一个串的时候,把last清成root,往里面加的时候,如果last指针往下走的时候已经有节点了,就需要拓展一个新的节点出来,否则就不满足广义后缀自动机的性质.此外,每一个节点代表的不一定是一个串,可能代表的是多个串的子串,所以要在每个点后面挂链,来表示这个节点是属于哪几个串中的子串.后面的事情就比较简单了,把后缀树建立出来,弄出DFS序,离线处所有询问,变成在一段序列中出现过

BZOJ 2780: [Spoj]8093 Sevenk Love Oimaster [广义后缀自动机]

JZPGYZ - Sevenk Love Oimaster     Oimaster and sevenk love each other.       But recently,sevenk heard that a girl named ChuYuXun was dating with oimaster. As a woman's nature, sevenk felt angry and began to check oimaster's online talk with ChuYuXun

【BZOJ2780】[Spoj]8093 Sevenk Love Oimaster 广义后缀自动机

[BZOJ2780][Spoj]8093 Sevenk Love Oimaster Description Oimaster and sevenk love each other.     But recently,sevenk heard that a girl named ChuYuXun was dating with oimaster.As a woman's nature, sevenk felt angry and began to check oimaster's online t

BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )

一个串a在b中出现, 那么a是b的某些前缀的后缀, 所以搞出AC自动机, 按fail反向建树, 然后查询(x, y)就是y的子树中有多少是x的前缀. 离线, 对AC自动机DFS一遍, 用dfs序+树状数组维护, DFS到的查询点就回答询问.时间复杂度O(|ACAM|+QlogQ) ------------------------------------------------------------------------------------------- #include<cstdio>

【BZOJ】1047: [HAOI2007]理想的正方形(单调队列/~二维rmq+树状数组套树状数组)

http://www.lydsy.com/JudgeOnline/problem.php?id=1047 树状数组套树状数组真心没用QAQ....首先它不能修改..而不修改的可以用单调队列做掉,而且更快,只有O(n^2).而这货是n^2log^2n的建树...虽然查询是log^2n...但是建树那里就tle了.. 那么说题解... 先orz下,好神.. 我怎么没想到单调队列orz 首先我们维护 行 的单调队列,更新每个点在 列 距离内的最小and最大的值 然后我们维护 列 的单调队列,更新每个点

SPOJ TEMPLEQ - Temple Queues(二分查找+树状数组)

题意: 有N个队伍(1 <= N <= 100,000),每个队伍开始有ai个人[0 <= ai<= 100,000,000],有Q个操作[0<=Q<= 500,000] 操作分为三种,1 A:表示在第A个队列加一个人. 2 X:表示求长度大于等于X队列数量.3 Y:表示所有长度大于等于Y的队列减去一个人. 题解: 把各个队列按长度排序 用差分数列来维护这个数组,这样求每个队列的长度就是求前缀和.每次求长度的复杂度是lgn,因为队列是按长度排序的,所以可以通过二分查找到

BZOJ 3594 [Scoi2014]方伯伯的玉米田(二维树状数组)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3594 [题目大意] 给出一个数列,选出k个区间使得区间内数全部加1, 求k次操作之后最长的不下降子序列 [题解] 我们发现,每次的区间右端点一定贪心选到最右端才是最优的, 那么在用树状数组统计的时候,只要一个点被+1了,那么后面的点起码+1, 我们在树状数组后面加一维统计被区间包含的次数,发现也是前缀和关系, 所以用二维树状数组统计答案即可. 为避免自身被重复统计,第二维循环降序.

bzoj 1669: [Usaco2006 Oct]Hungry Cows饥饿的奶牛【dp+树状数组+hash】

最长上升子序列.虽然数据可以直接n方但是另写了个nlogn的 转移:f[i]=max(f[j]+1)(a[j]<a[i]) O(n^2) #include<iostream> #include<cstdio> using namespace std; const int N=5005; int n,a[N],f[N],ans; int read() { int r=0,f=1; char p=getchar(); while(p>'9'||p<'0') { if(

【BZOJ2780】【Spoj8093】 Sevenk Love Oimaster 后缀自动机

#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43015849"); } 题意: n,m n个串 m个串 样例里面倒数第二行的you应该扔到下一行. 问m个串每个在前n个串中的几个出现过. 题解: 首先这道题跟 [BZOJ2754][SCOI2012]喵星球上的点名 是一样的,只不过更卡时一点,或者