• 【LOJ】#2064. 「HAOI2016」找相同字符


    题解

    做后缀自动机题要一点脑洞,脑洞一开,就过了

    我们显然要拿第二个串跑第一个串的后缀自动机

    我们可以求出第二个串每个位置匹配到的节点,和匹配的长度L

    那么我们统计一个后缀树上的根缀和,表示这样个节点的路径字符串的所有后缀在串中出现过多少次(路径字符串就是根到这个点的路径中等于这个节点len值的串)
    统计方法是p->sum = p->par->sum + p->cnt * (p->len - p->par->len)

    然后我们匹配到的位置不一定完整匹配了路径字符串,所以我们第二个串某个位置能匹配到的串的个数是
    p->par->sum + p->cnt * (L - p->par->len)

    代码

    #include <bits/stdc++.h>
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define pii pair<int,int>
    #define fi first
    #define se second
    #define MAXN 200005
    #define pb push_back
    //#define ivorysi
    using namespace std;
    typedef long long int64;
    typedef double db;
    template<class T>
    void read(T &res) {
        res = 0;T f = 1;char c = getchar();
        while(c < '0' || c > '9') {
            if(c == '-') f = -1;
            c = getchar();
        }
        while(c >= '0' && c <= '9') {
            res = res * 10 + c - '0';
            c = getchar();
        }
        res *= f;
    }
    template<class T>
    void out(T x) {
        if(x < 0) {x = -x;putchar('-');}
        if(x >= 10) out(x / 10);
        putchar('0' + x % 10);
    }
    struct node {
        node *nxt[26],*par;
        int len,cnt;
        int64 sum;
    }pool[MAXN * 2],*tail = pool,*root,*last,*que[MAXN * 2];
    char s1[MAXN],s2[MAXN];
    int N1,N2,c[MAXN];
    void build_sam(int l,int c) {
        node *nowp = tail++,*p;
        nowp->len = l;nowp->cnt = 1;
        for(p = last ; p && !p->nxt[c] ; p = p->par) {
            p->nxt[c] = nowp;
        }
        if(!p) nowp->par = root;
        else {
            node *q = p->nxt[c];
            if(q->len == p->len + 1) nowp->par = q;
            else {
                node *copyq = tail++;
                *copyq = *q;
                copyq->len = p->len + 1;copyq->cnt = 0;
                q->par = nowp->par = copyq;
                for(; p && p->nxt[c] == q ; p = p->par) {
                    p->nxt[c] = copyq;
                }
            }
        }
        last = nowp;
    }
    void Solve() {
        scanf("%s",s1 + 1);
        scanf("%s",s2 + 1);
        N1 = strlen(s1 + 1);N2 = strlen(s2 + 1);
        root = last = tail++;
        for(int i = 1 ; i <= N1 ; ++i) build_sam(i,s1[i] - 'a');
        int m = tail - pool;
        for(int i = 0 ; i < m ; ++i) c[pool[i].len]++;
        for(int i = 1 ; i <= N1 ; ++i) c[i] += c[i - 1];
        for(int i = 0 ; i < m ; ++i) que[c[pool[i].len]--] = &pool[i];
        for(int i = m ; i >= 1 ; --i) {
            if(que[i]->par) que[i]->par->cnt += que[i]->cnt;
        }
        for(int i = 2 ; i <= m ; ++i) {
            que[i]->sum = que[i]->par->sum + 1LL * (que[i]->len - que[i]->par->len) * que[i]->cnt;
        }
        int l = 0;
        node *p = root;
        int64 ans = 0;
        for(int i = 1 ; i <= N2 ; ++i) {
            int a = s2[i] - 'a';
            if(p->nxt[s2[i] - 'a']) {p = p->nxt[a];++l;}
            else {
                while(p->par && !p->nxt[a]) p = p->par;
                if(p->nxt[a]) {l = p->len + 1;p = p->nxt[a];}
                else {l = 0;}
            }
            if(p != root) ans += p->par->sum + 1LL * (l - p->par->len) * p->cnt;
        }
        out(ans);enter;
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        Solve();
        return 0;
    }
    

    哦这道题看起来是比NOI2018D1T3的68分难一点的
    但是我都是半个小时以内想出来了
    真讽刺

  • 相关阅读:
    多态及鸭子类型
    面向对象三大特性之——继承
    类的组合
    类的成员和命名空间
    JAVA中常用的类
    JAVA自学笔记(5)
    JAVA自学笔记(4)
    JAVA自学笔记(3)
    JAVA自学笔记(2)
    JAVA自学笔记(1)
  • 原文地址:https://www.cnblogs.com/ivorysi/p/9513866.html
Copyright © 2020-2023  润新知