Gym 101840BBreaking the Curse (SAM+二分)
Posted zhangbuang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gym 101840BBreaking the Curse (SAM+二分)相关的知识,希望对你有一定的参考价值。
Problem:
给定两个字符串s1和s2,q次查询,每次查询s1中的一段区间[L,R]中有多少个子串在s2中出现过
SOLUTION:
从头开始考虑,我们想知道字串的数量,那我们知道以s1每一个字符为结尾的最长公共字串后,他的所有的后缀都是一个字串
因此
对s2建sam,用s1跑最长公共子串,得到每个位置i往左最多可以匹配到的位置d[i]。显然d[i]是递减。于是对于每个查询,二分得到哪些位置的d[i]是越过L的,这些位置的贡献可以一起得到。剩下的位置的贡献用前缀和维护即可。
CODE:
#include<bits/stdc++.h> using namespace std; #define I inline #define fi first #define se second #define pb push_back #define ALL(X) (X).begin(), (X).end() #define CLR(A, X) memset(A, X, sizeof(A)) typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PII; const int N = 1e5+10, SIGMA = 26; int d[N]; LL sum[N]; struct SAM int last, sz; int ch[N<<1][SIGMA], len[N<<1], f[N<<1]; void init() for(int i = 1; i <= sz; i++) CLR(ch[i], 0); last = sz = 1; f[1] = len[1] = 0; int cal(int p, int c) int q = ch[p][c]; if(len[q] == len[p]+1) return q; int nq = ++sz; len[nq] = len[p]+1; memcpy(ch[nq], ch[q], sizeof(ch[q])); f[nq] = f[q]; f[q] = nq; while(p && ch[p][c]==q) ch[p][c] = nq, p = f[p]; return nq; void insert(int c) int p = last; if(ch[p][c]) last = cal(p, c); else int np = last = ++sz; len[np] = len[p]+1; while(!ch[p][c] && p) ch[p][c] = np, p = f[p]; if(!p) f[np] = 1; else f[np] = cal(p, c); void solve(char *s) int sl = strlen(s+1), u = 1, l = 0; for(int i = 1; i <= sl; i++) int c = s[i]-‘a‘; while(u!=1 && !ch[u][c]) u = f[u], l = len[u]; if(ch[u][c]) u = ch[u][c], l++; d[i] = i-l+1; sum[i] = sum[i-1]+l; A; char s1[N], s2[N]; I void work() A.init(); scanf("%s%s", s1+1, s2); int len = strlen(s2); for(int i = 0; i < len; i++) A.insert(s2[i]-‘a‘); A.solve(s1); int q; scanf("%d", &q); static int cas = 0; printf("Case %d:\n", ++cas); while(q--) int l, r; scanf("%d%d", &l, &r); int L = l, R = r; while(L < R) int M = (L+R)>>1; if(d[M] < l) L = M+1; else R = M; LL ans = 1LL*(L-l)*(L-l+1)/2+sum[r]-sum[L-1]; printf("%lld\n", ans); int main() if(fopen("curse.in", "r")) freopen("curse.in", "r", stdin); int X; scanf("%d", &X); while(X--) work(); return 0;
以上是关于Gym 101840BBreaking the Curse (SAM+二分)的主要内容,如果未能解决你的问题,请参考以下文章
2016 USP-ICMC-Codeforces-Gym101063C-Sleep Buddies Gym101063F-Bandejao Gym101063J-The Keys
Gym102156J The Good, the Bad and the Ugly
Gym 100247AThe Power of the Dark Side