NOI 2018 你的名字

因为机房里的小伙伴都在看《你的名字。》而我不想看

所以来写了这道题...

 

给一个 $S$ 串,$q$ 次询问,每次一个 $T$ 串,问 $T$ 有多少没在 $S[l,r]$ 中以子串形式出现过的本质不同的子串

$|S|,q \leq 5e5,\sum |T| \leq 5e5$

sol:

容斥一下就变成了 $T$ 与 $S[l,r]$ 有多少本质不同的公共子串

首先把 $T$ 串在 $S$ 串上跑,跑不动就跳 $parent$,这样可以预处理出 $T$ 的每一个前缀能在 $S[l,r]$ 中匹配的最长后缀长度 $len_i$,具体就用可持久化线段树合并维护每个点的 $right$ 集合,然后每次维护一个 $now$,查这个点的 $right$ 集合里有没有 $[l+now-1,r]$ 即可,这样做的话发现跑的节点和这个 $now$ 好像构成了一个双指针的样子,所以复杂度是对的

然后在 $T$ 串的后缀自动机上按每个状态记录答案即可

#include <bits/stdc++.h>
#define LL long long
#define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i)
#define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i)
using namespace std;
inline int read() {
    int x = 0,f = 1; char ch = getchar();
    for(; !isdigit(ch); ch = getchar())if(ch == '-') f = -f;
    for(; isdigit(ch); ch = getchar())x = 10 * x + ch - '0';
    return x * f;
}
const int maxn = 2e6 + 10;
int n, q;
char s[maxn];
int root[maxn], ToT;
struct Node {
    int val;
    int ls, rs;
} st[maxn << 6];
inline void Insert(int &x, int l, int r, int pos) {
    if(!x) x = ++ToT; if(l == r) return (void) (st[x].val++);
    int mid = (l + r) >> 1;
    (pos <= mid) ? Insert(st[x].ls, l, mid, pos) : Insert(st[x].rs, mid+1, r, pos);
    return (void) (st[x].val = st[st[x].ls].val + st[st[x].rs].val);
}
inline int query(int x, int l, int r, int L, int R) {
    if(L <= l && r <= R) return st[x].val;
    int mid = (l + r) >> 1, ans = 0;
    if(L <= mid) ans += query(st[x].ls, l, mid, L, R);
    if(R > mid) ans += query(st[x].rs, mid+1, r, L, R);
    return ans;
}
inline int merge(int x, int y, int l, int r) {
    if(!x || !y) return x + y;
    int z = ++ToT;
    if(l == r) st[z].val = st[x].val + st[y].val;
    else {
        int mid = (l + r) >> 1;
        st[z].ls = merge(st[x].ls, st[y].ls, l, mid);
        st[z].rs = merge(st[x].rs, st[y].rs, mid+1, r);
        st[z].val = st[st[z].ls].val + st[st[z].rs].val;
    }
    return z;
}
vector<int> G[maxn]; int ans[maxn];
namespace SAM1 {
    int first[maxn], to[maxn << 1], nx[maxn << 1], cnt;
    void add(int u, int v) {
        to[++cnt] = v;
        nx[cnt] = first[u];
        first[u] = cnt;
    }
    int rot, last, dfn;
    int tr[maxn][26], fa[maxn], mxlen[maxn];
    void extend(int c, int id) {
        int p = last, np = last = ++dfn;
        mxlen[np] = mxlen[p] + 1; Insert(root[np], 1, n, id);
        for(; p && !tr[p][c]; p = fa[p]) tr[p][c] = np;
        if(!p) fa[np] = rot;
        else {
            int q = tr[p][c];
            if(mxlen[q] == mxlen[p] + 1) fa[np] = q;
            else {
                int nq = ++dfn;
                mxlen[nq] = mxlen[p] + 1;
                memcpy(tr[nq], tr[q], sizeof(tr[nq]));
                fa[nq] = fa[q]; fa[q] = fa[np] = nq;
                for(; p && tr[p][c] == q; p = fa[p]) tr[p][c] = nq;
            }
        }
    }
    void build() {
        rep(i, 2, dfn) {
            add(fa[i], i);
            //cout << i << " " << fa[i] << endl;
        }
    }
    void dfs(int x) {
        for(int i=first[x];i;i=nx[i]) dfs(to[i]), root[x] = merge(root[x], root[to[i]], 1, n);
    }
    void Query(char *s, int l, int r) {
        int len = strlen(s + 1); int p = rot, now = 0;
        for(int i = 1; i <= len; i++){
            int c = s[i] - 'a';
            for(; p && !tr[p][c]; p = fa[p], now = mxlen[p]);
            if(!p) { p = rot, now = 0; continue; };
            p = tr[p][c], now++;
            while(p > 1) {
                if(query(root[p], 1, n, l + now - 1, r)) break;
                now--;
                if(now == mxlen[fa[p]]) p = fa[p];
            }
            if(p == rot) continue;
            for(int j = 0; j < (int) G[i].size(); j++) {
                ans[G[i][j]] = max(ans[G[i][j]], now);
                //cout << now << endl;
            }
        }
    }
} // namespace SAM1
namespace SAM2 {
    int rot, last, dfn;
    int tr[maxn][26], fa[maxn], mxlen[maxn];
    void init() {
        rep(i, 1, dfn) {
            ans[i] = fa[i] = mxlen[i] = 0;
            memset(tr[i], 0, sizeof(tr[i]));
        } rot = last = dfn = 1;
    }
    void extend(int c, int id) {
        int p = last, np = last = ++dfn;
        mxlen[np] = mxlen[p] + 1; G[id].push_back(np);
        for(; p && !tr[p][c]; p = fa[p]) tr[p][c] = np;
        if(!p) fa[np] = rot;
        else {
            int q = tr[p][c];
            if(mxlen[q] == mxlen[p] + 1) fa[np] = q;
            else {
                int nq = ++dfn;
                G[id].push_back(nq);
                mxlen[nq] = mxlen[p] + 1;
                fa[nq] = fa[q]; fa[q] = fa[np] = nq;
                memcpy(tr[nq], tr[q], sizeof(tr[q]));
                for(; p && tr[p][c] == q; p = fa[p]) tr[p][c] = nq;
            }
        }
    }
    LL Query() {
        LL ans1 = 0, ans2 = 0;
        rep(i, 1, dfn) {
            if(ans[i] > mxlen[fa[i]]) ans2 += min(ans[i], mxlen[i]) - mxlen[fa[i]];
            ans1 += mxlen[i] - mxlen[fa[i]];
        //    cout << ans[i] << endl;
            //cout << ans1 << " " << ans2 << endl;
        }
        return ans1 - ans2;
    }
} // namespace SAM2
int main() {
    SAM1::rot = SAM1::last = ++SAM1::dfn;
    scanf("%s", s + 1); n = strlen(s + 1);
    rep(i, 1, n) SAM1::extend(s[i] - 'a', i);
    SAM1::build(); SAM1::dfs(1);
    q = read();
    while(q--) {
        scanf("%s", s + 1); int l = read(), r = read();
        int len = strlen(s + 1); 
        rep(i, 1, len) G[i].clear();
        SAM2::init();
        rep(i, 1, len) SAM2::extend(s[i] - 'a', i);
        SAM1::Query(s, l, r);
        printf("%lld\n", SAM2::Query());
    }
}
View Code

 

posted @ 2019-04-04 22:59  探险家Mr.H  阅读(266)  评论(0编辑  收藏  举报