牛客多校第三场 H 题题解

Hacker

https://ac.nowcoder.com/acm/contest/33188/H

题意:给定长度为 nn 的模式串 SS,和长度为 mm 的权值数组 {wi}\{w_i\}。对于一个长度为 mm 的串,wiw_i 表示使用该串上第 ii 个字符与 SS 匹配可以获得 wiw_i 的权值。kk 次询问一个长度为 mm 的串 TTSS 的最大权值公共子串。n,m,k1×105,mq1×106n ,m,k\leq 1\times 10^5,mq\leq 1\times10^6

解法:对 SS 建立 SAM,然后对于每次询问都可以将 TT 串拉上去跑,对于 TT 串,每匹配一个字符,就可以通过 SAM 知道当前匹配了多长的字符串。此题为 SAM 经典应用之最长公共子串问题。因而问题转化成为了对于每个右端点 rr,有一个左边界 ll,然后问处于某个区间的最大连续子段和。维护 ww 的前缀和,利用区间查询最小值的线段树即可通过。复杂度 O(n+nlogn)\mathcal O(n+n\log n)

#include <bits/stdc++.h>
using namespace std;
long long inf = 0x3f3f3f3f3f3f3f3fll;
class segment_tree
{
    int n;
    vector<long long> minimum;
    long long query(int place, int left, int right, int start, int end)
    {
        if (start <= left && right <= end)
            return minimum[place];
        long long ans = inf;
        int mid = (left + right) >> 1;
        if (start <= mid)
            ans = min(ans, query(place << 1, left, mid, start, end));
        if (end > mid)
            ans = min(ans, query(place << 1 | 1, mid + 1, right, start, end));
        return ans;
    }

public:
    void set(int n, vector<long long> &w)
    {
        this->n = n;
        minimum.resize(4 * n + 10);
        function<void(int, int, int)> build = [&](int place, int left, int right)
        {
            if (left == right)
            {
                minimum[place] = w[left];
                return;
            }
            int mid = (left + right) >> 1;
            build(place << 1, left, mid);
            build(place << 1 | 1, mid + 1, right);
            minimum[place] = min(minimum[place << 1], minimum[place << 1 | 1]);
        };
        build(1, 0, n);
    }
    long long query(int l, int r)
    {
        return query(1, 0, n, l, r);
    }
} tr;
vector<long long> pre;
class SAM
{
    const int shift = 97;
    struct node
    {
        int ch[26];
        int len;
        int father;
        long long cnt;
        node()
        {
            memset(ch, 0, sizeof(ch));
            len = father = cnt = 0;
        }
    } NIL;
    vector<node> t;
    int last, ind;
    void insert(int c)
    {
        int p = last;
        int np = last = ++ind;
        t.push_back(NIL);
        t[np].len = t[p].len + 1;
        t[np].cnt = 1;
        for (; p && !t[p].ch[c]; p = t[p].father)
            t[p].ch[c] = np;
        if(!p)
            t[np].father = 1;
        else
        {
            int q = t[p].ch[c];
            if (t[p].len + 1 == t[q].len)
                t[np].father = q;
            else
            {
                int nq = ++ind;
                t.push_back(t[q]);
                t[nq].cnt = 0;
                t[nq].len = t[p].len + 1;
                t[q].father = t[np].father = nq;
                for (; p && t[p].ch[c] == q; p = t[p].father)
                    t[p].ch[c] = nq;
            }
        }
    }
 
public:
    SAM(string s)
    {
        last = ind = 1;
        t.push_back(NIL);
        t.push_back(NIL);
        for (auto i : s)
            insert(i - shift);
    }
    long long query(string &s)
    {
        int place = 1, len = 0, n = s.length();
        long long ans = 0;
        for (int i = 0; i < n;i++)
        {
            while (place > 1 && !t[place].ch[s[i] - shift])
            {
                place = t[place].father;
                len = t[place].len;
            }
            if (!t[place].ch[s[i] - shift])
                continue;
            place = t[place].ch[s[i] - shift];
            len++;
            ans = max(ans, pre[i + 1] - tr.query(i + 1 - len, i + 1));
        }
        return ans;
    }
};
int main()
{
    cin.tie(0)->sync_with_stdio(0);
    cin.exceptions(cin.failbit);
    cin.tie(NULL);
    cout.tie(NULL);
    int n, m, k;
    long long x;
    cin >> n >> m >> k;
    pre.resize(m + 1);
    string s, t;
    cin >> s;
    SAM solve(s);
    for (int i = 1; i <= m;i++)
    {
        cin >> x;
        pre[i] = pre[i - 1] + x;
    }
    tr.set(n, pre);
    for (int i = 1; i <= k;i++)
    {
        cin >> t;
        cout << solve.query(t) << "\n";
    }
    return 0;
}
全部评论

相关推荐

头像
11-27 14:28
长沙理工大学
刷算法真的是提升代码能力最快的方法吗?&nbsp;刷算法真的是提升代码能力最快的方法吗?
牛牛不会牛泪:看你想提升什么,代码能力太宽泛了,是想提升算法能力还是工程能力? 工程能力做项目找实习,算法也分数据结构算法题和深度学习之类算法
点赞 评论 收藏
分享
11-01 20:03
已编辑
门头沟学院 算法工程师
Amazarashi66:这种也是幸存者偏差了,拿不到这个价的才是大多数
点赞 评论 收藏
分享
评论
1
收藏
分享
牛客网
牛客企业服务