【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分难一点的
但是我都是半个小时以内想出来了
真讽刺

原文地址:https://www.cnblogs.com/ivorysi/p/9513866.html