2021 icpc 沈阳 M(后缀自动机)
题意:给定一个串串,求每个前缀的子串中字典序最大子串出现在的最左边的位置。
思路:其实看到子串看到字典序,很容易联想到桂林的那个J,很自然的反串建后缀自动机,然后建好parent树,边权是较父亲刚好多出的字符,然后dfs序就是字典序了,具体看上一篇桂林的博客。之后怎么求呢,考虑每一个子串的贡献,也就是对于每一个子串,以他为最大字典序的子串的右端点一定在它的右端点右边(好像一句废话),那么我们在parent数上沿dfs序递增遍历的时候,由于子串字典序越来越大了,那么当前子串的贡献必然可以覆盖前面的所有子串,也就是当前子串的右端点右边的所有后缀都会被当前子串覆盖,可以线段树维护,也可以搞个标记数组,记录下time,最后覆盖下就行,记录覆盖信息的时候我们很容易想到对于一个子串,其最大字典序子串必然是其后缀,所以我们只需要记录左端点就行了。注意,为了能够尽量覆盖到多的子串,我们在构建后缀自动机的时候对每一个子串的位置应该尽量大,因为我们是反串建的,所以对于原串就是尽量小,这样就可以尽可能覆盖到多的子串。
吐槽一下赛中的窒息操作,忘记输入,倒置字符串的时候1到n交换,结果等于没转,tim从0开始记的,最后最小值也定了个0,结果tim为0的信息都没获取到。
#include
#define de(x) cerr << " debug " << #x << " == " << x << endl;
#define ll long long
using namespace std;
const int maxn = 1e6 + 7;
struct state
{
int len, link;
int nxt[26];
int id;
int pos;
} st[maxn * 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].len = st[last].len + 1;
st[cur].pos = id;
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].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;
}
int len[maxn*2];
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 str;
int x;
node(int ss, int xx)
{
str = ss;
x = xx;
}
};
vector v[maxn*2];
bool cmp(node a, node b)
{
return a.str < b.str;
}
pair P[maxn];
int tim;
void dfs(int x)
{
if (x != 0)
{
int l = n - st[x].pos + 1;
int r = l + st[st[x].link].len;
P[r] = make_pair(tim++, l);
}
for(auto i: v[x])
{
dfs(i.x);
}
// for (int i = 0; i < 26; i++)
// {
// if (st[x].nxt[i])
// {
// dfs(st[x].nxt[i]);
// }
// }
}
int main()
{
scanf("%s",s+1);
n = strlen(s + 1);
for (int i = 1; i <= n/2; i++)
{
swap(s[i], s[n - i + 1]);
}
sam_init();
for (int i = 1; i <= n; i++)
{
sam_extend(s[i], i);
}
getSiz();
for (int i = 1; i < sz; i++)
{
v[st[i].link].push_back(node(s[st[i].pos - st[st[i].link].len] - 'a', i));
}
for (int i = 0; i < sz; i++)
{
sort(v[i].begin(), v[i].end(), cmp);
}
dfs(0);
int ans = 1;
int tt = 0;
// cout< tt)
{
tt = P[i].first;
ans = P[i].second;
}
printf("%d %d\n", ans, i);
}
#ifdef iyua
system("pause");
#endif
return 0;
}