BZOJ 1396 识别子串

BZOJ 1396 识别子串

昨晚拿到这道题想到了线段树做法还以为麻烦了,码了好久过了才发现网上都这么写的。。

只出现一次实际上就是 endpos 集合大小为1,就是 parent 树上siz是1。

由于我们只需要 endpos 集合大小是1的点的 endpos 集合,也没必要线段树合并求 endpos 集合了,对每个点加进去的时候记录一下 endpos 。由于我们只需要集合大小为 1 的,更新覆盖一下没关系。

然后现在拿到这些点后,假设这些点的 endpos 为 $ en $ ,这个点上最长串长度为 $ len $ 最短串为 $ minlen = len[par[u]] + 1 $ ,其对于 $ en - minlen + 1 $ 到 $ en $ 这一段贡献是 $ minlen $ 对于 $ en - len $ 到 $ en - minlen $ 的贡献是一个一次函数( $ en - pos $ ),其实这个一次函数我们只需要记录 $ en $ ,最后也只需要找到最小的 $ en $ 就好了。

可以开两棵线段树做,其实也可以用 $ set $ 类似差分的做吧,不过线段树毕竟很板子。(只是码得多一点)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
#define MAXN 200006
 
int n , lst , m;
char ch[MAXN];
int ans[MAXN];
 
struct seg {
    int T[100000 << 2] , lz[100000 << 2];
    void build( ) {
        memset( T , 0x3f , sizeof T ) , memset( lz , 0x3f , sizeof lz );
    }
    void pd( int rt ) {
        if( lz[rt] != 0x3f3f3f3f ) {
            T[rt << 1] = min( T[rt << 1] , lz[rt] );
            T[rt << 1 | 1] = min( T[rt << 1 | 1] , lz[rt] );
            lz[rt << 1] = min( lz[rt << 1] , lz[rt] );
            lz[rt << 1 | 1] = min( lz[rt << 1 | 1] , lz[rt] );
            lz[rt] = 0x3f3f3f3f;
        }
    }
    void chkmn( int rt , int l , int r , int L , int R , int c ) {
        if( L <= l && R >= r ) { lz[rt] = min( lz[rt] , c ) , T[rt] = min( T[rt] , c ); return; }
        pd( rt );
        int m = l + r >> 1;
        if( L <= m ) chkmn( rt << 1 , l , m , L , R , c );
        if( R > m ) chkmn( rt << 1 | 1 , m + 1 , r , L , R , c);
    }
    void getit( int rt , int l , int r ) {
        if( l == r ) { ans[l] = min( ans[l] , T[rt] ); return; }
        pd( rt );
        int m = l + r >> 1;
        getit( rt << 1 , l , m ) , getit( rt << 1 | 1 , m + 1 , r );
    }
} T[2] ;
 
struct SAM{
    int son[MAXN][27]; // sons
    int par[MAXN] , len[MAXN]; // node
    int head[MAXN] , nxt[MAXN<<1] , to[MAXN<<1]; // edge
    int cnt , ecnt;
    int s[MAXN];
    void adde( int u , int v ) {
        to[++ecnt] = v;
        nxt[ecnt] = head[u];
        head[u] = ecnt;
    }
    void init(  ) {
        memset( son , 0 , sizeof son ) , memset( head , -1 , sizeof head );
        cnt = lst = 1; ecnt = 0;
    }
    void addall(  ) { 
        for( int i = 2 ; i <= cnt ; ++ i ) adde( par[i] , i );
    }
    int en[MAXN];
    void ins( int x , int i ) {
        int cur = ++ cnt; en[cur] = i;
        len[cur] = len[lst] + 1;
        int p = lst;
        while( p && !son[p][x] ) son[p][x] = cur , p = par[p];
        if( !p ) par[cur] = 1;
        else {
            int q = son[p][x];
            if( len[q] == len[p] + 1 ) par[cur] = q;
            else {
                int cl = ++ cnt;
                en[cl] = en[q];
                memcpy( son[cl] , son[q] , sizeof son[q] );
                par[cl] = par[q]; 
                len[cl] = len[p] + 1 , par[q] = par[cur] = cl;
                for( ; son[p][x] == q ; p = par[p] ) son[p][x] = cl;
            }
        }
        lst = cur;
    }
    void dfs( int u ) {
        if( head[u] == -1 ) {
            int t = en[u] , l = len[u] , l1 = len[par[u]];
            T[0].chkmn( 1 , 1 , n , t - l1 + 1 , t , l1 );
            T[1].chkmn( 1 , 1 , n , t - l + 1 , t - l1 , t );
        }
        for( int i = head[u] ; ~i ; i = nxt[i] ) {
            int v = to[i];
            dfs( v );
        }
    }
} S ;
namespace wtf {
 
    int main() {
        S.init();
        scanf("%s",ch + 1);
        n = strlen( ch + 1 );
        S.init( );
        for( int i = 1 ; i <= n ; ++ i ) S.ins( ch[i] - 'a' , i );
        T[0].build() , T[1].build();
        S.addall();
        S.dfs( 1 );
        memset( ans , 0x3f , sizeof ans );
        T[1].getit( 1 , 1 , n );
        for( int i = 1 ; i <= n ; ++ i ) {
            ans[i] = ans[i] - i;
        }
        T[0].getit( 1 , 1 , n );
        for( int i = 1 ; i <= n ; ++ i )
            printf("%d\n",ans[i] + 1);
    }
}
int main() {
    wtf::main();
}
上一篇:2021-03-15


下一篇:长度最小的子数组