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 - 101147 C.The Wall

Gym 100247AThe Power of the Dark Side

2018 ACM-ICPC World Finals Gym-102482C Conquer the World

gym 101064 G.The Declaration of Independence (主席树)