2022-06-30 19:55阅读: 18评论: 0推荐: 0

Codeforces 1037H. Security

传送门
Difficulty:3200

题目大意

一个串 s(1|s|2105)q(112105) 次询问,每次询问 l,r(1lrn) 和一个串 x(1|x|2105) ,求 s[l:r] 中字典序最小的子串 s ,有 s>x ,没有则输出 1

思路

如果没有 l,r 的限制,我们可以建立 ssam ,在 sam 上贪心地走,先找出最长的与 x 的公共前缀,然后考虑再加一个尽可能小的字符作为答案,如果不行则不断回退一个字符再进行尝试,最后如果都不行即为 1 。有了 l,r 的限制后我们需要让当前串始终为 s[l:r] 的子串,设当前子串的长度为 sz ,如果其所在节点的 right 集合中存在一个值 v[l+sz1,r] ,就说明当前子串在 s[l:r] 中,我们可以用线段树合并来维护各节点的 right 集合中存在哪些值,判断是否满足条件即在节点对应的线段树上查询 [l+sz1,r] 内是否有值。线段树中不用维护任何信息,查询时只要看对应的节点在线段树中是否存在即可。

代码

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mk make_pair
//#define int LL
//#define lc P*2
//#define rc P*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 1000000009;
const LL mod = 998244353;
const int maxn = 200010; 

int cnt = 0, root[maxn * 30], N;
struct sgt {
    int lc, rc;
}tr[maxn * 30];

int build()
{
    ++cnt;
    tr[cnt].lc = tr[cnt].rc = 0;

    return cnt;
}

void modify(int p, int l, int r, int val)
{
    if (l + 1 == r)
        return;
    int mid = (l + r) / 2;
    if (val < mid)
    {
        if (!tr[p].lc)
            tr[p].lc = build();
        modify(tr[p].lc, l, mid, val);
    }
    else
    {
        if (!tr[p].rc)
            tr[p].rc = build();
        modify(tr[p].rc, mid, r, val);
    }
}

bool query(int p, int l, int r, int a, int b)
{
    if (!p)
        return false;
    if (l >= a && r <= b)
        return true;
    int mid = (l + r) / 2;
    if (b <= mid)
        return query(tr[p].lc, l, mid, a, b);
    else if (a >= mid)
        return query(tr[p].rc, mid, r, a, b);
    else
        return query(tr[p].lc, l, mid, a, mid) | query(tr[p].rc, mid, r, mid, b);
}

int merge(int p, int q, int l, int r)
{
    if (!p)
        return q;
    if (!q)
        return p;
    int ne = build();
    if (l + 1 == r)
        return ne;
    int mid = (l + r) / 2;
    tr[ne].lc = merge(tr[p].lc, tr[q].lc, l, mid);
    tr[ne].rc = merge(tr[p].rc, tr[q].rc, mid, r);

    return ne;
}

int tot = 1, last = 1;

struct Node {
    int len, fa;
    int ch[26];
    bool isnp;
}sam[maxn * 3];

void extend(char c)
{
    int p = last, np = last = ++tot;
    sam[np].len = sam[p].len + 1;
    sam[np].isnp = true;
    root[np] = build(), modify(root[np], 1, N + 1, sam[np].len);
    for (; p && !sam[p].ch[c]; p = sam[p].fa)
        sam[p].ch[c] = np;
    if (!p)
        sam[np].fa = 1;
    else
    {
        int q = sam[p].ch[c];
        if (sam[q].len == sam[p].len + 1)
            sam[np].fa = q;
        else
        {
            int nq = ++tot;
            sam[nq] = sam[q], sam[nq].len = sam[p].len + 1;
            sam[nq].isnp = false;
            root[nq] = build();
            sam[q].fa = sam[np].fa = nq;
            for (; p && sam[p].ch[c] == q; p = sam[p].fa)
                sam[p].ch[c] = nq;
        }
    }
}

vector<int>G[maxn * 3];

void add_edge(int from, int to)
{
    G[from].push_back(to);
}

void dfs(int v)
{
    for (auto& to : G[v])
        dfs(to), root[v] = merge(root[v], root[to], 1, N + 1);
}

string S;
int Q;

void solve()
{
    N = S.length(), root[1] = build();
    for (int i = 0; i < N; i++)
        extend(S[i] - 'a');
    for (int i = 2; i <= tot; i++)
        add_edge(sam[i].fa, i);
    dfs(1);
    string T, ans;
    int l, r, M, sz, now;
    while(Q--)
    {
        cin >> l >> r >> T;
        stack<int>stk;
        ans = "", now = 1, sz = 0;
        M = T.length(), stk.push(now);
        while(true)
        {
            if (sz >= M)
                break;
            int to = sam[now].ch[T[sz] - 'a'];
            if (to && query(root[to], 1, N + 1, l + sz, r + 1))
                now = to, stk.push(to), ans += T[sz++];
            else
                break;
        }
        bool flag = false;
        while (!stk.empty())
        {
            now = stk.top();
            stk.pop();
            int s;
            if (sz >= M)
                s = 0;
            else
                s = T[sz] - 'a' + 1;
            for (int i = s; i < 26; i++)
            {
                int to = sam[now].ch[i];
                if (to && query(root[to], 1, N + 1, l + sz, r + 1))
                {
                    ans += i + 'a', flag = true;
                    break;
                }
            }
            if (flag || stk.empty())
                break;
            ans.pop_back(), sz--;
        }
        cout << (flag ? ans : "-1") << endl;
    }
}

int main()
{
    IOS;
    cin >> S >> Q;
    solve();

    return 0;
}

本文作者:Prgl

本文链接:https://www.cnblogs.com/Prgl/p/16427132.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Prgl  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开