zoukankan      html  css  js  c++  java
  • BZOJ 3277 串

    BZOJ 3277 串

    首先建立广义SAM,然后考虑SAM上一个节点是多少个串的子串。

    这是一个从 bzoj 2780 学来的做法,就是建立广义SAM后对于每一个串在SAM上跑出每个前缀所在的节点,这个可以直接转移,然后从这些节点分别跳parent,直到跳到一个已经被这个串以前的点跳到过的点,并把跳到的点所属的词++。

    这样看上去很 $ O(n^2) $ 但实际上它跑的跟 $ O(n) $ 似的快。

    不太会证明QAQ,有会证这个下界的评论一下吧~

    现在我们可以再拿这些串跑一遍跑到的点如果拥有的词多余 k 我们就把它的所有 parent 树上祖先的拥有的词多余 k 的子串数和给得到。意思就是以某个字符串中的位置为后缀得到的所有满足条件的子串。可以一次dp算出来,也可以直接记忆化比较好写。

    就酱

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    #define MAXN 400006
    
    int n , lst , m;
    char ch[MAXN];
    
    struct SAM{
        int son[MAXN][27]; // sons
        int par[MAXN] , len[MAXN]; // node
        int cnt;
        int s[MAXN];
        void init(  ) {
            memset( son , 0 , sizeof son );
            cnt = lst = 1;
        }
        void ins( int x ) {
            int cur = ++ cnt;
            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;
                    memcpy( son[cl] , son[q] , sizeof son[q] );
                    par[cl] = par[q]; // copy
                    len[cl] = len[p] + 1 , par[q] = par[cur] = cl;
                    for( ; son[p][x] == q ; p = par[p] ) son[p][x] = cl;
                }
            }
            lst = cur;
        }
        int vis[MAXN];
        void add( int cur , int i ) {
            ++ s[cur] , vis[cur] = i;
            if( par[cur] && vis[par[cur]] != i ) add( par[cur] , i );
        }
        int dp[MAXN];
        long long work( int cur , int i ) {
            if( dp[cur] ) return dp[cur];
            long long ret = 0;
            if( s[cur] >= m ) ret += len[cur] - len[par[cur]];
            if( par[cur] ) return dp[cur] = ret + work( par[cur] , i );
            return dp[cur] = ret;
        }
    } S ;
    namespace wtf {
        int brk[MAXN], len[MAXN];
    
        void main() {
            cin >> n >> m;
            S.init();
            int tot = 0;
            for (int i = 1; i <= n; ++i) {
                lst = 1;
                brk[i] = tot;
                scanf("%s", ch + tot), len[i] = strlen(ch + tot);
                for (int j = 0; j < len[i]; ++j) S.ins(ch[tot + j] - 'a');
                tot += len[i];
            }
            int cur;
            for (int i = 1; i <= n; ++i) {
                cur = 1;
                for (int j = brk[i]; j < brk[i] + len[i]; ++j)
                    cur = S.son[cur][ch[j] - 'a'], S.add(cur, i);
            }
            memset( S.vis , 0 , sizeof S.vis );
            for (int i = 1; i <= n; ++i) {
                cur = 1;
                long long res = 0;
                for (int j = brk[i]; j < brk[i] + len[i]; ++j)
                    cur = S.son[cur][ch[j] - 'a'], res += S.work(cur, i);
                printf("%lld ",res);
            }
        }
    }
    int main() {
        wtf::main();
    }
    
  • 相关阅读:
    Cannot resolve org.springframework:spring-web:5.2.2.BUILD-SNAPSHOT
    阿里规范最新泰山版下载
    Eureka启动连接报错Connect Refused
    SpringCloud集成feign和swagger导致feign报NullPointException
    js select 默认回显判断
    js 相差年、月、日
    mysql导出PDM文件步骤
    eclipse 安装反编译工具
    判断 List map set 是否为空
    mysql 5.7 版本的安装
  • 原文地址:https://www.cnblogs.com/yijan/p/bzoj3277.html
Copyright © 2011-2022 走看看