• 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
    ", SAM2::Query());
        }
    }
    View Code
  • 相关阅读:
    JAVA多线程实现的三种方式
    Java String charAt()方法
    三大数据库分页方法
    SSH框架 spring 配置中的: scope="prototype"
    SSH中的Invalid action class configuration that references an unknown class named.......
    String类中toCharArray()方法的用法
    SQL Server中DateTime与DateTime2的区别
    Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例
    Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例
    哈希表及处理冲突的方法
  • 原文地址:https://www.cnblogs.com/Kong-Ruo/p/10657465.html
Copyright © 2020-2023  润新知