版权声明:本文为博主原创文章,转载请附上原博客链接。 https://blog.csdn.net/Dale_zero/article/details/82951788
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6194
查询原串中出现次数等于k次的子串数量。需要用到基数排序。
构造完后缀自动机之后将节点按照maxlen值从小到大进行排序。
因为某个节点p的父节点fp的maxlen值一定比p的maxlen小(因为其right集合比p大,right(p)是right(fp)的子集),所以fp一定排在p前面。
最初始将每个节点的cnt值赋为1,表示该节点代表子串在原串中的出现次数(或者说是right集合的元素数量,一个意思)。排完序之后倒着遍历,将每个节点的cnt值加到其父节点上(因为一个串出现了k次,其后缀一定也出现了k次)。并且因为后缀自动机的性质,拥有相同父节点的节点们的right集合没有交集,所以不会重复计算。
顺便更新了一波模板
模板:
const int N = 4e5+10;///N为字符串长,后缀自动机节点数不超过2N-2所以要左移一位
char s[N];
const int LetterSize = 26;
int tot, last,ch[N<<1][LetterSize],fa[N<<1],len[N<<1];
int sum[N<<1],tp[N<<1];///tp【i】:max值为第i小的节点序号。
int root=1;
void init()
{
last = tot = 1;
len[1] = 0;
memset(ch,0,sizeof ch);
memset(fa,0,sizeof fa);
}
void add( int x)
{
int p = last, np = last = ++tot;
len[np] = len[p] + 1;
while( p && !ch[p][x]) ch[p][x] = np, p = fa[p];
if(p == 0) fa[np] = 1;
else
{
int q = ch[p][x];
if( len[q] == len[p] + 1)
fa[np] = q;
else
{
int nq = ++tot;
memcpy( ch[nq], ch[q], sizeof ch[q]);
len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq;
while( p && ch[p][x] == q) ch[p][x] = nq, p = fa[p];
}
}
}
void toposort()
{
for(int i = 1; i <= len[last]; i++) sum[i] = 0;
for(int i = 1; i <= tot; i++) sum[len[i]]++;
for(int i = 1; i <= len[last]; i++) sum[i] += sum[i-1];
for(int i = 1; i <= tot; i++) tp[sum[len[i]]--] = i;
}
题目代码:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 1e5+10;
char s[N];
const int LetterSize = 26;
int tot, last,ch[N<<1][LetterSize],fa[N<<1],len[N<<1];
LL cnt[N<<1];
int sum[N<<1],tp[N<<1];///tp【i】:max值为第i小的节点序号。
void init()
{
last = tot = 1;
len[1] = 0;
memset(ch,0,sizeof ch);
memset(fa,0,sizeof fa);
memset(cnt,0,sizeof cnt);
}
void add( int x)
{
int p = last, np = last = ++tot;
len[np] = len[p] + 1;
cnt[last]=1;
while( p && !ch[p][x]) ch[p][x] = np, p = fa[p];
if(p == 0) fa[np] = 1;
else
{
int q = ch[p][x];
if( len[q] == len[p] + 1)
fa[np] = q;
else
{
int nq = ++tot;
memcpy( ch[nq], ch[q], sizeof ch[q]);
len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq;
while( p && ch[p][x] == q) ch[p][x] = nq, p = fa[p];
}
}
}
void toposort()
{
for(int i = 1; i <= len[last]; i++) sum[i] = 0;
for(int i = 1; i <= tot; i++) sum[len[i]]++;
for(int i = 1; i <= len[last]; i++) sum[i] += sum[i-1];
for(int i = 1; i <= tot; i++) tp[sum[len[i]]--] = i;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
LL k;
init();
scanf("%lld",&k);
scanf("%s",s);
for(int i=0;s[i];i++)
{
add(s[i]-'a');
}
toposort();
LL ans=0;
for(int i=tot;i>=1;i--)
{
int p=tp[i],fp=fa[p];
cnt[fp]+=cnt[p];
if(cnt[p]==k)
ans+=len[p]-len[fp];
}
printf("%lld\n",ans);
}
return 0;
}