2021 ccpc桂林 J - Suffix Automaton(后缀自动机+线段树)
作者:互联网
题目地址
赛中到最后时间不够,写的时候脑袋混乱,后来因为不明错误样例都没调过...没拿金我背大锅。
赛后重新整理下思路,直接A了,哎....
思路:首先对子串字典序问题有个经典做法是反串建后缀自动机,然后建出parent树,其中在建树时边权是子节点比父节点刚好多出一个的字符,这样这个树的dfs序就刚好是子串的字典序了,然后题目中是在子串长度相等的基础上比较字典序,否则比较长度,那么对于树上的某一个节点,由于其代表的子串长度是连续的,所以可以利用差分的思想,将这个子串的信息存到差分数组的该子串的最短长度和最长长度+1上,代表这段区间的长度含这个子串。然后就先按从小到大离线询问,然后对于每个询问,算出这个询问代表的子串的长度k和在这个长度里排第几x,然后将差分数组的指针一步步移到当前长度,一边移动一边更新线段树(将相应的dfs序更新),然后查询线段树中第x个出现的下标即可。由于最后答案要求是最左边出现的子串的下标,也就是要求反串后最右下标,可以从parent树从叶子往上更新位置的最大值。
越想越气...
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#include <set>
#include <stack>
#include <time.h>
#include <map>
#include <algorithm>
#include <fstream>
//#include <unordered_map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1000000 + 100;
const int INF = 0x7fffffff;
const int mod = 998244353;
const ll mod1 = 998244353;
const ll base = 137;
const double Pi = acos(-1.0);
const int G = 3;
struct state
{
int len, link;
//std::map<char, int> next;
int nxt[26];
int pos;
};
const int MAXLEN = 1000000 + 5;
state st[MAXLEN * 2];
int sz, last;
void sam_init()
{
memset(st[0].nxt, 0, sizeof(st[0].nxt));
sz = 0;
st[0].len = 0;
st[0].link = -1;
sz++;
last = 0;
}
int cur;
void sam_extend(char c, int id)
{
cur = sz++;
st[cur].pos = id;
st[cur].len = st[last].len + 1;
int p = last;
while (p != -1 && !st[p].nxt[c - 'a'])
{
st[p].nxt[c - 'a'] = cur;
p = st[p].link;
}
if (p == -1)
{
st[cur].link = 0;
}
else
{
int q = st[p].nxt[c - 'a'];
if (st[p].len + 1 == st[q].len)
{
st[cur].link = q;
}
else
{
int clone = sz++;
st[clone].len = st[p].len + 1;
memcpy(st[clone].nxt, st[q].nxt, sizeof(st[clone].nxt));
// st[clone].pos = st[q].pos;
//st[clone].next = st[q].next;
st[clone].link = st[q].link;
while (p != -1 && st[p].nxt[c - 'a'] == q)
{
st[p].nxt[c - 'a'] = clone;
p = st[p].link;
}
st[q].link = st[cur].link = clone;
}
}
last = cur;
//siz[cur]=1;
}
int len[maxn];
int a[maxn*2];
int n;
void getSiz()
{
for(int i=1;i<sz;i++)
{
len[st[i].len]++;
}
for(int i=n;i>=1;i--)
{
len[i]+=len[i+1];
}
for(int i=1;i<sz;i++)
{
a[len[st[i].len]--]=i;
}
for(int i=1;i<sz;i++)
{
int p=a[i];
st[st[p].link].pos=max(st[st[p].link].pos,st[p].pos);
}
}
char s[maxn];
struct node
{
int id;
ll x;
} qu[maxn];
bool cmp(node a, node b)
{
return a.x < b.x;
}
struct NNode
{
int to;
int str;
NNode(int tt = 0, int ss = 0)
{
to = tt;
str = ss;
}
};
vector<NNode> edge[maxn];
int in[maxn * 2];
int vis[maxn * 2];
int cnt;
void dfs(int x)
{
in[x] = ++cnt;
vis[cnt] = x;
//cout<<222<<' '<<cnt<<' '<<x<<endl;
for (int i = 0; i < (int)edge[x].size(); i++)
{
dfs(edge[x][i].to);
}
}
struct pp
{
int id;
int f;
pp(int ii, int ff)
{
id = ii;
f = ff;
}
};
vector<pp> v[maxn];
ll sum[maxn];
pair<int, int> ans[maxn];
struct Node
{
int l, r, sum;
} tree[maxn * 8];
void build(int i, int l, int r)
{
//cout<<i<<' '<<l<<' '<<r<<endl;
tree[i].l = l;
tree[i].r = r;
if (l == r)
{
tree[i].sum = 0;
return;
}
int mid = (l + r) >> 1;
build(i * 2, l, mid);
build(i * 2 + 1, mid + 1, r);
tree[i].sum = 0;
}
void update(int i, int k, int x)
{
if (tree[i].l == tree[i].r)
{
tree[i].sum += x;
return;
}
if (tree[i * 2].r >= k)
update(i * 2, k, x);
if (tree[i * 2 + 1].l <= k)
update(i * 2 + 1, k, x);
tree[i].sum = tree[i * 2].sum + tree[i * 2 + 1].sum;
}
int query(int i, int k)
{
// cout<<i<<' '<<tree[i].l<<' '<<tree[i].r<<' '<<tree[i*2].sum<<' '<<k<<endl;
if (tree[i].l == tree[i].r)
{
return tree[i].l;
}
int res;
if (tree[i * 2].sum >= k)
res = query(i * 2, k);
else
res = query(i * 2 + 1, k - tree[i * 2].sum);
return res;
}
int dp[maxn];
bool cmp2(NNode a, NNode b)
{
return a.str < b.str;
}
int main()
{
scanf("%s", s + 1);
int len = strlen(s + 1);
n=len;
for (int i = 1; i <= len / 2; i++)
{
swap(s[i], s[len - i + 1]);
}
sam_init();
for (int i = 1; i <= len; i++)
sam_extend(s[i], i);
getSiz();
// printf("%s",s+1);
for (int i = 1; i < sz; i++)
{
edge[st[i].link].push_back(NNode(i, s[st[i].pos - st[st[i].link].len] - 'a'));
// cout<<222<<' '<<st[i].pos<<' '<<st[st[i].link].len<<' '<<s[st[i].pos-st[st[i].link].len]-'a'<<endl;
}
for (int i = 0; i < sz; i++)
{
sort(edge[i].begin(), edge[i].end(), cmp2);
}
dfs(0);
for (int i = 1; i < sz; i++)
{
v[st[st[i].link].len + 1].push_back(pp(i, 0));
v[st[i].len + 1].push_back(pp(i, 1));
dp[st[st[i].link].len + 1]++;
dp[st[i].len + 1]--;
}
for (int i = 1; i <= len; i++)
{
sum[i] = sum[i - 1] + dp[i];
// cout<<111<<' '<<i<<' '<<sum[i]<<endl;
}
for (int i = 1; i <= len; i++)
{
sum[i] += sum[i - 1];
}
int q;
scanf("%d", &q);
for (int i = 1; i <= q; i++)
{
scanf("%lld", &qu[i].x);
qu[i].id = i;
}
sort(qu + 1, qu + 1 + q, cmp);
//cout<<len<<endl;
build(1, 1, sz);
int now = 1;
for (int i = 1; i <= q; i++)
{
int le = lower_bound(sum + 1, sum + 1 + len, qu[i].x) - sum;
int k = qu[i].x - sum[le - 1];
// cout<<le<<' '<<k<<endl;
if (le > len)
{
ans[qu[i].id].first = -1;
ans[qu[i].id].second = -1;
continue;
}
while (now <= le)
{
for (auto j : v[now])
{
if (j.f == 0)
{
update(1, in[j.id], 1);
// cout<<in[j.id]<<' '<<1<<endl;
}
else
{
update(1, in[j.id], -1);
}
}
now++;
}
int p = query(1, k);
// cout<<111<<' '<<k<<' '<<vis[p]<<endl;
p=vis[p];
// while(st[st[p].link].len>=le) p=st[p].link;
int l = st[p].pos - le + 1;
int r = st[p].pos;
ans[qu[i].id].first = len - r + 1;
ans[qu[i].id].second = len - l + 1;
}
for (int i = 1; i <= q; i++)
{
printf("%d %d\n", ans[i].first, ans[i].second);
}
// system("pause");
}
标签:Suffix,int,ccpc,len,st,link,2021,maxn,include 来源: https://www.cnblogs.com/lijiayi123/p/15557743.html