zoukankan      html  css  js  c++  java
  • Gym 101840B Breaking 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:
    ", ++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
    ", ans);
        }
    }
    
    int main() {
        if(fopen("curse.in", "r"))
            freopen("curse.in", "r", stdin);
        int X; scanf("%d", &X);
        while(X--) work();
        return 0;
    }
    

      

  • 相关阅读:
    hdu 6188 Duizi and Shunzi
    区间第k大
    AtCoder Regular Contest 081 E
    hdu 6170 Two strings
    hdu 6156 Palindrome Function
    2017百度之星初赛(B)-1006-小小粉丝度度熊 hdu 6119
    AtCoder Regular Contest 080 E
    hdu 6069 Counting Divisors
    hdu 6058 Kanade's sum (多校3)
    苹果曼和树
  • 原文地址:https://www.cnblogs.com/zhangbuang/p/11360334.html
Copyright © 2011-2022 走看看