牛客多校第四场 I.string(后缀自动机+回文树)

问一个串内,能选出多大的一个子串集合,满足两两子串之间互不相同而且不存在一个子串是另一个子串的反串。

首先显然,重复出现的子串只能选一个,也就是只能选本质不同的子串。
于是考虑将原串和其反串建立广义sam,此时原反串相同的部分全部压缩在了一起,对这个sam统计本质不同的子串个数,此时的统计结果中,符合题目条件(反串不同)的子串统计了两次(正串一次反串一次),不符合题目条件的串统计了一次。但是显然回文串和单个字符会被过滤掉,但是回文和单个字符都符合题意(因为它是自己把自己过滤掉的而不是被别的过滤掉的)。此时应该统计本质不同的回文个数(回文树节点数减去奇根偶根),再加回去。
我们再重新理一下每个子串被统计了几次,符合题目条件(反串不同)的子串统计了两次(正串一次反串一次),不符合题目条件的串被统计了一次,但是只能选一半(正反最多选其一),再加上本质不同回文串,回文串此时也被统计了两次,也就是说所有要统计的串都是他要被统计的次数的两倍!
因此此时统计个数直接除2,就是答案。

#include 

using namespace std;
typedef long long ll;

const int maxn = 400005;

char s[maxn];

struct Pam {
    int next[maxn][26];
    int fail[maxn];
    int len[maxn];// 当前节点表示回文串的长度
    int S[maxn];
    int last, n, p;

    int newNode(int l) {
        memset(next[p], 0, sizeof(next[p]));
        len[p] = l;
        return p++;
    }

    void init() {
        n = last = p = 0;
        newNode(0);
        newNode(-1);
        S[n] = -1;
        fail[0] = 1;
    }

    int getFail(int x) {
        while (S[n - len[x] - 1] != S[n]) {
            x = fail[x];
        }
        return x;
    }

    void add(int c) {
        S[++n] = c;
        int cur = getFail(last);
        if (!next[cur][c]) {
            int now = newNode(len[cur] + 2);
            fail[now] = next[getFail(fail[cur])][c];
            next[cur][c] = now;
        }
        last = next[cur][c];
    }

    int build() {
        init();
        for (int i = 0; s[i]; i++) {
            add(s[i] - 'a');
        }
        return p - 2;
    }
} pam;

struct Sam {
    int next[maxn << 1][26];
    int link[maxn << 1], step[maxn << 1];
    unordered_set<int> mark[maxn << 1];
    int sz, last, root;

    void init() {
        //如多次建立自动机,加入memset操作
        root = sz = last = 1;
    }

    void add(int c) {
        if (next[last][c] && step[last] + 1 == step[next[last][c]]) {
            last = next[last][c];
            return;
        }

        int p = last;
        int np = ++sz;
        last = 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];
                }
            }
        }
    }

 
    void build() {
        init();
        int len = strlen(s);
        for (int i = 0; i < len; ++i) {
            add(s[i] - 'a');
        }
        last = 1;
        ll tot = 0;
        for (int i = len - 1; i >= 0; --i) {
            add(s[i] - 'a');
        }
        for (int i = root + 1; i <= sz; ++i) {
            tot += 1LL * step[i] - step[link[i]];
        }
        ll ans = tot + pam.build();
        printf("%lld\n", ans / 2);
    }
} sam;

//暴力对拍
int brute() {
    string e(s);
    set<string> st;
    for (int i = 0; i < e.length(); ++i) {
        for (int j = i + 1; j <= e.length(); ++j) {
            st.insert(e.substr(i, j - i));
        }
    }
    vector<string> ee;
    for (auto i : st) {
        ee.push_back(i);
    }
    int cnt = 0;
    for (int i = 0; i < ee.size(); ++i) {
        string tt = ee[i];
        reverse(tt.begin(), tt.end());
        for (int j = 0; j < ee.size(); ++j) {
            if (i == j) {
                continue;
            }
            if (tt == ee[j]) {
                ++cnt;
            }
        }
    }
    cnt /= 2;
    cout << ee.size() - cnt << endl;
}

int main() {
    scanf("%s", s);
    sam.build();
//    brute();
    return 0;
}

非广义sam做法:

#include 

using namespace std;
typedef long long ll;

const int maxn = 400005;

char s[maxn];

struct Pam {
    int next[maxn][26];
    int fail[maxn];
    int len[maxn];// 当前节点表示回文串的长度
    int S[maxn];
    int last, n, p;

    int newNode(int l) {
        memset(next[p], 0, sizeof(next[p]));
        len[p] = l;
        return p++;
    }

    void init() {
        n = last = p = 0;
        newNode(0);
        newNode(-1);
        S[n] = -1;
        fail[0] = 1;
    }

    int getFail(int x) {
        while (S[n - len[x] - 1] != S[n]) {
            x = fail[x];
        }
        return x;
    }

    void add(int c) {
        S[++n] = c;
        int cur = getFail(last);
        if (!next[cur][c]) {
            int now = newNode(len[cur] + 2);
            fail[now] = next[getFail(fail[cur])][c];
            next[cur][c] = now;
        }
        last = next[cur][c];
    }

    int build() {
        init();
        for (int i = 0; s[i]; i++) {
            add(s[i] - 'a');
        }
        return p - 2;
    }
} pam;

struct Sam {
    int next[maxn << 1][26];
    int link[maxn << 1], step[maxn << 1];
    int a[maxn], b[maxn << 1];
    int count[maxn << 1];
    int sz, last, root;

    void init() {
        //如多次建立自动机,加入memset操作
        root = sz = last = 1;
    }

    void add(int c) {
        int p = last;
        int np = ++sz;
        last = 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];
                }
            }
        }
    }

    void build() {
        init();
        int len = strlen(s);
        for (int i = 0; i < len; ++i) {
            add(s[i] - 'a');
        }

        ll tot = 0;
        int now = root, cnt = 0;
        for (int i = len - 1; i >= 0; --i) {
            while (!next[now][s[i] - 'a']) {
                now = link[now];
                cnt = step[now];
            }
            now = next[now][s[i] - 'a'], ++cnt;
            count[now] = max(count[now], cnt);
        }

        for (int i = 1; i <= sz; i++) {
            a[step[i]]++;
        }
        for (int i = 1; i <= step[last]; i++) {
            a[i] += a[i - 1];
        }
        for (int i = 1; i <= sz; i++) {
            b[a[step[i]]--] = i;
        }
        for (int i = sz; i > root; --i) {
            int e = b[i];
            if(count[e]) {
                count[link[e]] = step[link[e]];
                tot += 1LL * count[e] - step[link[e]];
            }
        }

        ll ans = 0;
        for (int i = root + 1; i <= sz; ++i) {
            ans += 1LL * step[i] - step[link[i]];
        }
        int cctt = pam.build();
        ans = ans - (tot + cctt) / 2 + cctt;
        printf("%lld\n", ans);
    }
} sam;

vector<string> ee, ee2;

int brute() {
    string e(s);
    set<string> st;
    for (int i = 0; i < e.length(); ++i) {
        for (int j = i + 1; j <= e.length(); ++j) {
            st.insert(e.substr(i, j - i));
        }
    }
    for (auto i : st) {
        ee.push_back(i);
    }
    int cnt = 0;
    for (int i = 0; i < ee.size(); ++i) {
        string tt = ee[i];
        reverse(tt.begin(), tt.end());
        for (int j = 0; j < ee.size(); ++j) {
            if (i == j) {
                continue;
            }
            if (tt == ee[j]) {
                ++cnt;
                ee2.push_back(ee[j]);
            }
        }
    }
    cnt /= 2;
    cout << ee.size() - cnt << endl;
}

int main() {
    scanf("%s", s);
//    brute();
    sam.build();
    return 0;
}

你可能感兴趣的:(ACM,后缀自动机,回文树)