[SPOJ8222] - Substrings(NSUBSTR)- 后缀自动机

本文介绍了解决SPOJ-NSUBSTR问题的方法,通过构建后缀自动机并结合基数排序,实现了高效计算字符串中不同长度子串出现次数的最大值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说在前面

并没有什么好说的,但是要保持格式


题目

SPOJ - NSUBSTR传送门

题目大意

给出一个长度为N的仅包含小写字母的字符串(N不超过250000)。定义函数 F(x) ,表示所有长度为x的子串 在原串中出现次数的最大值.举个栗子:ababa中,aba出现了两次,是长度为3的子串中出现次数最多的,于是 F(3)=2
现在需要求出 F(1...N)

输入输出格式

输入格式:
仅一行,包含一个字符串

输出格式:
输出N行,第i行表示 F(i)


解法

这是一道比较基础的后缀自动机题=w=
首先明显有 F(x)=max(F(x),F(x+1))
因为righ集合表示的是当前串在哪里出现过,所以只需要求出每个right集合的大小
很明显,每个right集合的大小就等于它parent树上的叶子结点个数。于是建出后缀自动机,基数排序之后,用每个节点去更新它的parent,然后枚举节点去更新 F(len) ,这样单个的F(len)就求出来了。最后从大到小更新一遍就是答案


下面是自带大常数的代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , ans[250005] ;
char ss[250005] ;
struct Node{
    int len , cnt ;
    Node *ch[26] , *par ;
}w[500005] , *tw = w , *root , *last ;

void newNode( Node *&nd , int len ){
    nd = ++tw ;
    nd->len = len ;
}

void Insert( const char &cc ){
    Node *nd , *tmp = last ;
    newNode( nd , tmp->len + 1 ) ; nd->cnt = 1 ;
    short id = cc - 'a' ;

    for( ; tmp && !tmp->ch[id] ; tmp = tmp->par )
        tmp->ch[id] = nd ;
    if( !tmp ) nd->par = root ;
    else{
        Node *B = tmp->ch[id] , *nB ;
        if( tmp->len + 1 == B->len ) nd->par = B ;
        else{
            newNode( nB , tmp->len + 1 ) ;
            memcpy( nB->ch , B->ch , sizeof( nB->ch ) ) ;
            nB->par = B->par ;
            B->par = nd->par = nB ;
            while( tmp && tmp->ch[id] == B ){
                tmp->ch[id] = nB ;
                tmp = tmp->par ;
            }
        }
    }
    last = nd ;
}

int tp[250005] , sa[500005] ;
void Rsort(){
    for( int i = tw - w ; i ; i -- ) tp[ w[i].len ] ++ ;
    for( int i = 1 ; i <= N ; i ++ ) tp[i] += tp[i-1] ;
    for( int i = tw - w ; i ; i -- ) sa[ tp[w[i].len]-- ] = i ;
}

void solve(){
    for( int i = tw - w ; i > 1 ; i -- ){
        Node *nd = ( w + sa[i] ) ;
        nd->par->cnt += nd->cnt ;
        ans[ nd->len ] = max( ans[ nd->len ] , nd->cnt ) ;
    }
    for( int i = N ; i ; i -- )
        ans[i] = max( ans[i] , ans[i+1] ) ;
    for( int i = 1 ; i <= N ; i ++ )
        printf( "%d\n" , ans[i] ) ;

}

int main(){
    newNode( root , 0 ) ; last = root ;
    scanf( "%s" , ss + 1 ) ; N = strlen( ss + 1 ) ;
    for( int i = 1 ; i <= N ; i ++ ) Insert( ss[i] ) ;
    Rsort() ; solve() ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值