HihoCoder - 1445 后缀自动机二·重复旋律5 后缀自动机

后缀自动机二·重复旋律5


时间限制: 10000ms
单点时限: 2000ms
内存限制: 512MB
描述

小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一个音乐旋律被表示为一段数构成的数列。

现在小Hi想知道一部作品中出现了多少不同的旋律?

解题方法提示

输入

共一行,包含一个由小写字母构成的字符串。字符串长度不超过 1000000。

输出

一行一个整数,表示答案。

样例输入
aab
样例输出
5


Source

HihoCoder - 1445


My Solution

题意:统计字符串中不同子串的个数。


后缀自动机

这题是后缀自动机基础题,直接使用SAM上的pre树, sigma{val[np] - val[pre[np]]}

复杂度 O(n)


#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
using namespace std;
typedef long long LL;
const int MAXN = 2*1e6 + 8;

string s;
struct SAM{
    int ch[MAXN][26], pre[MAXN], val[MAXN], endpos[MAXN];
    int last, tot;
    void init(){
        last = tot = 0;
        memset(ch[0], -1, sizeof ch[0]);
        pre[0] = -1; val[0] = 0;
    }
    int extend(int c, int ind){
        int p = last, np = ++tot;
        val[np] = val[p] + 1; endpos[np] = ind;
        memset(ch[np], -1, sizeof ch[np]);
        while(~p && ch[p][c] == -1) ch[p][c] = np, p = pre[p];
        if(p == -1) pre[np] = 0;
        else{
            int q = ch[p][c];
            if(val[q] != val[p] + 1){
                int nq = ++tot;
                memcpy(ch[nq], ch[q], sizeof ch[q]);
                val[nq] = val[p] + 1;
                pre[nq] = pre[q];
                pre[q] = pre[np] = nq;
                while(~p && ch[p][c] == q) ch[p][c] = nq, p = pre[p];
            }
            else pre[np] = q;
        }
        last = np;
        return val[np] - val[pre[np]];
    }
    /*
    vector<int> sons[2*MAXN];
    void build_the_tree(){
        for(int i = 1; i <= tot; i++){
            sons[pre[i]].push_back(i);
        }
    }
    */
} sam;

int main()
{
    #ifdef LOCAL
    freopen("21.in", "r", stdin);
    //freopen("21.out", "w", stdout);
    int T = 2;
    while(T--){
    #endif // LOCAL
    ios::sync_with_stdio(false); cin.tie(0);

    int n, i;
    LL sum =0;
    cin >> s;
    n = s.size();
    sam.init();
    for(i = 0; i < n; i++) sum += sam.extend(s[i] - 'a', i+1);
    cout << sum << endl;;


    #ifdef LOCAL
    cout << endl;
    }
    #endif // LOCAL
    return 0;
}


  Thank you

                                                                                                                                             ------from ProLights

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值