2020 牛客多校4 C (后缀自动机)
题意
给你一个字符串,有一个操作f,任意取一个区间[l,r],获得一个字符串,这个字符串第i个字符为[l,l + i -1],区间内的最大的字符。求经过所有可能的操作之后,所有本质不同的字符串的种数。
比如样例1,dbca,
从d开始有d,dd,ddd,dddd,
从b开始有b,bc,bcc,
从c开始有,c,cc,
从a开始有a
总共10种。
看了大佬的题解,发现原来只要简单改一改后缀自动机的操作就好(T▽T)。他的思路是从后往前遍历字符串,然后根据题意我们知道从i开始的后缀相比i + 1的后缀,在遇到比第i个字符大或相等的字符之前都会改变原先的前缀,但之后的所有字符都不会改变。所以我们只需要将改变了的字符加入后缀自动机就好。至于从那里开始加,可以用栈维护那个起始点,每次插入一个字符,然后再从插入之后的终点开始插入下一个。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e6 + 7;
struct Suffix_Automata
{
int maxlen[N << 1], trans[N << 1][12], link[N << 1], Size, Last;
Suffix_Automata() { Size = Last = 1; }
inline int Extend(int id, int last)
{
if(trans[last][id])//这里是专门添加的插入,除了有返回之外,剩下的与裸的后缀自动机一样
{
int q = trans[last][id], p = last;
if(maxlen[q] == maxlen[last] + 1) return q;
else
{
int clone = ++Size;
maxlen[clone] = maxlen[last] + 1;
memcpy(trans[clone], trans[q], sizeof(trans[clone]));
link[clone] = link[q];
for (; p && trans[p][id] == q; p = link[p]) trans[p][id] = clone;
link[q] = clone;
return clone;
}
}
int cur = (++Size), p;
maxlen[cur] = maxlen[last] + 1;
for (p = last; p && !trans[p][id]; p = link[p]) trans[p][id] = cur;
if (!p) link[cur] = 1;
else
{
int q = trans[p][id];
if (maxlen[q] == maxlen[p] + 1)
link[cur] = q;
else
{
int clone = (++Size);
maxlen[clone] = maxlen[p] + 1;
memcpy(trans[clone], trans[q], sizeof(trans[clone]));
link[clone] = link[q];
for (; p && trans[p][id] == q; p = link[p]) trans[p][id] = clone;
link[cur] = link[q] = clone;
}
}
last = cur;
return cur;
}
struct node{
int s, last;
};
stack<node> sta;
void solve()
{
char s[N];
scanf("%s", s + 1);
int len = strlen(s + 1);
for (int i = len; i >= 1;i--)//用栈维护第i个字符对之后后缀的改变,直到遇到大于等于x的字符(因为比它大的时候,后面的后缀与原先的相同)
{
int x = s[i] - 'a';
int del = 1, st;
while(sta.size() && sta.top().s < x) sta.pop(), del++;
if(!sta.size()) st = 1;
else st = sta.top().last;
while(del--)
{
st = Extend(x, st), sta.push({x, st});
}
}
ll ans = 0;
for (int i = 2; i <= Size;i++)//遍历各个节点,统计答案
{
ans += maxlen[i] - maxlen[link[i]];
}
printf("%lld\n", ans);
}
} T;
int main()
{
T.solve();
return 0;
}