SPOJ NSUBSTR Substrings 子串计数
vjudge传送门
题面:给以个长度为\(2.5*10^5\)的小写字母组成的字符串,令\(F(x)\)表示长度为\(x\)的子串出现次数的最大值,求\(F(i)\).
这个算是SAM的裸题吧。
先把SAM建出来,然后统计每一个节点的endpos个数\(siz[u]\),那么\(max \{siz[u] \}(len[u]=i)\)就是\(F(i)\)。
有人会问,这样只是把长度为\(len[u]\)的子串更新了,但是这个节点包含了长度在\([len[fa[u]]]+1,len[u]]\)的子串啊,这些子串没有更新!
事实上,这些子串虽然没有在这个节点被更新,也一定会在其他节点更新。
拿"bcaab"为例:假如这个节点存的串是"aab"到"bcaab",那么我们只更新了"bcaab"代表的长度,即\(F(5)\),而"caab"和"aab",即\(F(4)\)和\(F(3)\)都没有被更新。但是所有出现\(bcaab\)的位置,"bcaa"和"bca"(即前缀)也都出现了呀,所以可能在"bcaa"和"bca"所在的节点更新\(F(4)\)和\(F(3)\),而且这个值肯定不小于\(bcaab\)的出现次数。不过有可能"bcaa"也不是该等价类中长度最大的串,比如前面还有一个'a',我们仍然可以用“替换”的思想,将“bcaa”替换成“abcaa”中的“abca”,同样可以用来更新\(F(4)\).
值得一提的是,一直这么替换下去,不仅能保证最后会更新\(F(4)\),还保证了出现次数不递减,因为这个替换的条件就是被替换的串出现的位置替换后的串一定都会出现。
因此,最后求出的\(F(i)\)一定是出现次数的最大值。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
const int maxn = 2.5e5 + 5;
const int maxs = 27;
In ll read()
{
ll ans = 0;
char ch = getchar(), las = ' ';
while(!isdigit(ch)) las = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(las == '-') ans = -ans;
return ans;
}
In void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
char s[maxn];
int Max[maxn];
struct Sam
{
int tra[maxn << 1][maxs], link[maxn << 1], len[maxn << 1], siz[maxn << 1], cnt, las;
In void init() {link[cnt = las = 0] = -1;}
In void insert(int c)
{
int now = ++cnt, p = las;
len[now] = len[p] + 1, siz[now] = 1;
while(~p && !tra[p][c]) tra[p][c] = now, p = link[p];
if(p == -1) link[now] = 0;
else
{
int q = tra[p][c];
if(len[q] == len[p] + 1) link[now] = q;
else
{
int clo = ++cnt;
memcpy(tra[clo], tra[q], sizeof(tra[q]));
len[clo] = len[p] + 1;
link[clo] = link[q]; link[q] = link[now] = clo;
while(~p && tra[p][c] == q) tra[p][c] = clo, p = link[p];
}
}
las = now;
}
int buc[maxn << 1], pos[maxn << 1];
In void solve()
{
for(int i = 1; i <= cnt; ++i) buc[len[i]]++;
for(int i = 1; i <= cnt; ++i) buc[i] += buc[i - 1];
for(int i = 1; i <= cnt; ++i) pos[buc[len[i]]--] = i;
for(int i = cnt; i; --i)
{
int now = pos[i], fa = link[now];
siz[fa] += siz[now];
Max[len[now]] = max(Max[len[now]], siz[now]);
}
}
}S;
int main()
{
scanf("%s", s);
int n = strlen(s); S.init();
for(int i = 0; i < n; ++i) S.insert(s[i] - 'a');
S.solve();
// for(int i = n - 1; i; --i) Max[i] = max(Max[i], Max[i + 1]); //这句话不用加
for(int i = 1; i <= n; ++i) write(Max[i]), enter;
return 0;
}