2019HDU多校第8场1005.Acesrc and String Theory(为什么要写SAM?)

给一个串,求问有多少子串是由k个循环节组成的。

似乎是一种经典的搞循环节的做法…

对字符串进行枚举分块(从1到len/k枚举块的个数),然后判断有多少个连续的块是相同的。

得到了一片相同的块之后,我们再把这一段向左右扩展,对这几块前面的前缀部分,与这几块的任意一部分求个lcs(最长公共后缀),lcs的长度即为可向前拓展的部分。同理,对这几块后面的后缀与任意一块求一个lcp(最长公共前缀),lcp长度即为可拓展的部分,这样就找到了当前能找到的最长一个循环串。

这个串对答案的贡献是max(0,(lcp+lcs+1)+当前相同块个数 乘 当前块长 - k 乘 当前块长)

那么现在重点就变成了如何求任意两个后缀的lcp和lcs,如果用后缀数组求,非常的好写…
如果用sam写,你就要求两个节点lca…就非常恶心,而且dfs求dfs序的时候还会爆栈(parent树是一条链时深度=串长,比如aaaaaaa),于是要用栈模拟dfs。

然而你权神就是要写sam,下面这份令人绝望的代码就诞生了。

权神说要被夸!权神的rmqnb!

#include <bits/stdc++.h>

#define X first
#define Y second
//#pragma GCC optimize(2)
using namespace std;
typedef unsigned long long ll;

const int maxn = 3e5 + 5;

int _, k, len;
char s[maxn];
int Log[maxn << 2];

struct Sam {
    int next[maxn << 1][26];
    int link[maxn << 1], step[maxn << 1];
    int pos[maxn];
    vector<int> G[maxn << 1];
    int sz, last, root;

    void init() {
        //如多次建立自动机,加入memset操作
        for (int i = 0; i <= sz; ++i) {
            G[i].clear();
        }
        root = sz = last = 1;
        memset(next[root], 0, sizeof(next[root]));
    }

    void add(int c) {
        int p = last;
        int np = ++sz;
        last = np;


        memset(next[np], 0, sizeof(next[np]));
        step[np] = step[p] + 1;
        while (!next[p][c] && p) {
            next[p][c] = np;
            p = link[p];
        }

        if (p == 0) {
            link[np] = root;
        } else {
            int q = next[p][c];
            if (step[p] + 1 == step[q]) {
                link[np] = q;
            } else {
                int nq = ++sz;
                memcpy(next[nq], next[q], sizeof(next[q]));
                step[nq] = step[p] + 1;
                link[nq] = link[q];
                link[q] = link[np] = nq;
                while (next[p][c] == q && p) {
                    next[p][c] = nq;
                    p = link[p];
                }
            }
        }
    }

    int in[maxn << 1], tot;
    int p[maxn << 2], dep[maxn << 1];

    void dfs(int x, int fa) {
        in[x] = ++tot;
        p[tot] = x;
        for (int i = 0; i < G[x].size(); ++i) {
            int v = G[x][i];
            dep[v] = dep[x] + 1;
            dfs(v, x);
        }
//        p[++tot]=fa;
//        for (auto v:G[x]) {
//            dep[v] = dep[x] + 1;
//            dfs(v);
//            p[++tot] = x;
//        }
    }

    void dfss() {
        stack<pair<int, int>> st;
        st.push({root, 0});
        in[root] = ++tot;
        p[tot] = root;
        while (!st.empty()) {
            pair<int, int> x = st.top();
            st.pop();
            if (x.Y == G[x.X].size()) {
                if (st.size() > 0)
                    p[++tot] = st.top().X;
                continue;
            }
            st.push({x.X, x.Y + 1});
            st.push({G[x.X][x.Y], 0});
            in[G[x.X][x.Y]] = ++tot;
            dep[G[x.X][x.Y]] = st.size() - 1;
            p[tot] = G[x.X][x.Y];
        }
    }

    int rmq[maxn << 2][30];

    int cmp(int p1, int p2) {
        if (dep[p1] < dep[p2]) return p1;
        return p2;
    }

    void rbq() {
        for (int i = 1; i < tot; i++) {
            rmq[i][0] = cmp(p[i], p[i + 1]);
        }
        for (int i = 1; i <= 29; i++) {
            for (int j = 1; j <= tot; j++) {
                if ((1 << i - 1) + j > tot) break;
                rmq[j][i] = cmp(rmq[j][i - 1], rmq[(1 << i - 1) + j][i - 1]);
            }
        }
    }

    int getlca(int a, int b) {
        if (in[a] > in[b])
            swap(a, b);
        int l = in[b] - in[a];
        if (l == 0)
            return 1;
        int p = Log[l];
        return step[cmp(rmq[in[a]][p], rmq[in[b] - (1 << p)][p])];
    }

    void build() {
        init();
        tot = 0;
        for (int i = 0; s[i]; i++) {
            add(s[i] - 'a');
            pos[i] = last;
        }
        for (int i = sz; i > root; --i) {
            G[link[i]].push_back(i);
        }
//        memset(p,0, sizeof(p));
//        memset(dep,0, sizeof(dep));
        memset(in,0,sizeof(in));
//        dfs(root, 0);
        dfss();
        rbq();
    }
} s1, s2;


void solve() {
    ll ans = 0;
    for (int i = 1; i <= len / k; ++i) {
        int lcs = 0, cnt = 1, lcp;
        for (int j = 0; j < len; j += i) {
            if (j + i > len)
                lcp = 0;
            else
                lcp = s2.getlca(s2.pos[j], s2.pos[j + i]);
            if (lcp >= i) {
                ++cnt;
            } else {
                ans += max(lcp + lcs + 1 + cnt * i - k * i, 0);
                cnt = 1;
                if (j + 2 * i - 1 > len)
                    lcs = 0;
                else
                    lcs = s1.getlca(s1.pos[j + i - 1], s1.pos[j + 2 * i - 1]);
            }
        }
    }
    printf("%lld\n", ans);
}

/*

 1
 2
abababab*/
int main() {
    scanf("%d", &_);
    Log[0] = -1;
    for (int i = 1; i < maxn * 4; i++) {
        Log[i] = Log[i / 2] + 1;
    }
    while (_--) {
        scanf("%d%s", &k, s);
        len = strlen(s);
        if(k == 1){
            printf("%lld\n",1LL * len * (len + 1) / 2);
            continue;
        }
        s1.build();
        reverse(s, s + len);
        s2.build();
        reverse(s2.pos, s2.pos + len);
        solve();
    }
    return 0;
}

发布了156 篇原创文章 · 获赞 20 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/Cymbals/article/details/99681805