【gym 101991 E】(ACPC 2018) E. Exciting Menus 题解

文章目录

    • 题意
    • 题解
    • 代码

题意

  • N N N 个字符串,第 i i i 个字符串的第 k k k 个位置(从 1 1 1 开始)都有权值 A k i A_k^i Aki
  • 定义 Q ( i , j , k ) = p o p u l a r i t y ( S j , k i ) × A k i × ∣ S j , k i ∣ Q(i, j, k)=popularity(S_{j,k}^{i}) \times A_k^i \times |S_{j,k}^i| Q(i,j,k)=popularity(Sj,ki)×Aki×Sj,ki
    • p o p u l a r i t y ( s t r ) popularity(str) popularity(str) 表示字符串 s t r str str 是这 N N N 个字符串中多少个的前缀
    • S j , k i S_{j,k}^i Sj,ki 为第 i i i 个字符串的 j j j k k k 的子串
    • ∣ s t r ∣ |str| str 表示字符串 s t r str str 的长度
  • Q ( i , j , k ) Q(i, j, k) Q(i,j,k) 的最大值

1 ≤ N ≤ 1 0 5 1\le N \le 10^5 1N105,字符串长度之和 ≤ 1 0 5 \le 10^5 105 0 ≤ A k i ≤ 1 0 9 0\le A_k^i\le 10^9 0Aki109

题解

对着 N 个串建立 AC自动机。一个节点的 p o p u l a r i t y ( S j , k i ) , ∣ S j , k i ∣ popularity(S_{j,k}^{i}) ,|S_{j,k}^i| popularity(Sj,ki),Sj,ki 值是固定的。
在 fail 树上,一个节点的所有父亲节点的都会对当前匹配的字符串产生贡献,所以沿着 fail 树,预处理每一个节点到根的 p o p u l a r i t y ( S j , k i ) × ∣ S j , k i ∣ popularity(S_{j,k}^{i}) \times |S_{j,k}^i| popularity(Sj,ki)×Sj,ki 的最大值。
这样把这 N N N 个串分别在 AC 自动机上匹配一遍,顺便统计答案即可。

代码

#include 

#define mem(f, v) memset(f,v,sizeof(f))
#define file(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);

typedef long long ll;
const int maxn = 100010;
const int inf = ~(1u << 31u);
const ll linf = ~(1llu << 63u);

using namespace std;

vector<vector<int>> va;
vector<string> s;

struct ACAM_node {
    ll val;
    int num, dep;
    vector<ACAM_node *> ch;
    ACAM_node *fail;

    ACAM_node(int chSZ = 26) : num(0), val(0), fail(nullptr) {
        ch.resize(chSZ, nullptr);
    }
} po[maxn];

ACAM_node *get(int idx) {
    po[idx].val = po[idx].num = po[idx].dep = 0;
    po[idx].ch.clear(), po[idx].fail = nullptr;
    po[idx].ch.resize(26, nullptr);
    return po + idx;
}

struct ACAM {
    ACAM_node *root;
    int cnt;

    ACAM() : cnt(0) {
        root = get(cnt++);
    }

    void add(const string &str) {
        ACAM_node *now = root;
        for (auto c:str) {
            int idx = c - 'a';
            if (now->ch[idx] == nullptr) now->ch[idx] = get(cnt++);
            now->ch[idx]->num++;
            now = now->ch[idx];
        }
    }

    void build() {
        queue<ACAM_node *> q;
        root->fail = nullptr;
        for (auto &x: root->ch)
            if (x) {
                x->fail = root;
                x->dep = 1;
                x->val = (ll)x->dep * x->num;
                q.push(x);
            }else x = root;
        while (!q.empty()) {
            auto u = q.front();
            q.pop();
            for (int i = 0; i < u->ch.size(); i++) {
                auto &x = u->ch[i];
                if (x) {
                    x->fail = u->fail->ch[i];
                    x->dep = u->dep + 1;
                    x->val = max(x->fail->val, (ll) x->dep * x->num);
                    q.push(x);
                } else x = u->fail->ch[i];
            }
        }
    }

    ll query(const string &str, int index) {
        ACAM_node *now = root;
        ll mx = 0;
        for (int i = 0; i < str.length(); i++) {
            int idx = str[i] - 'a';
            now = now->ch[idx];
            mx = max(mx, now->val * va[index][i]);
        }
        return mx;
    }
};

int main() {
    ios::sync_with_stdio(false);
    freopen("exciting.in", "r", stdin);
    int T;
    cin >> T;
    while (T--) {
        s.clear(), va.clear();
        int n;
        cin >> n;
        for (int i = 0; i < n; i++) {
            string str;
            cin >> str;
            s.emplace_back(str);
        }
        for (int i = 0; i < n; i++) {
            vector<int> d;
            for (int j = 0, x; j < s[i].length(); j++) {
                cin >> x;
                d.emplace_back(x);
            }
            va.emplace_back(d);
        }
        ACAM acam;
        for (auto &x:s) acam.add(x);
        acam.build();
        ll res = 0;
        for (int i = 0; i < n; i++)
            res = max(res, acam.query(s[i], i));
        cout << res << "\n";
    }
    return 0;
}

你可能感兴趣的:(AC自动机)