4516: [Sdoi2016]生成魔咒

4516: [Sdoi2016]生成魔咒

链接

题意:

  求本质不同的子串。

分析:

  后缀数组或者SAM都可以。

  考虑SAM中每个点的可以表示的子串是一个区间min(S)~max(S),把每个点的这个区间加起来即可。

  字符集有点大,可以用map。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 200005;
map<int,int> ch[N];
int fa[N], len[N], Index = 1, Last = 1;
LL Ans;

void extend(int c) {
    int np = ++Index, p = Last; 
    len[np] = len[p] + 1;
    for (; p && ch[p].find(c) == ch[p].end(); p = fa[p]) ch[p][c] = np;
    if (!p) fa[np] = 1;
    else {
        int Q = ch[p][c];
        if (len[Q] == len[p] + 1) fa[np] = Q;
        else {
            int NQ = ++Index;
            fa[NQ] = fa[Q];
            ch[NQ] = ch[Q];
            len[NQ] = len[p] + 1;
            fa[Q] = fa[np] = NQ;
            for (; p && ch[p].find(c) != ch[p].end() && ch[p][c] == Q; p = fa[p]) ch[p][c] = NQ;
        }
    }
    Last = np;
    Ans += len[np] - len[fa[np]]; // max(np)=len[np], min(np)=len[fa[p]] + 1
    printf("%lld\n", Ans);
}
int main() {
    int n = read(); 
    for (int i = 1; i <= n; ++i) {
        int x = read();
        extend(x);
    }
    return 0;
}

 

posted @ 2019-02-12 21:49  MJT12044  阅读(159)  评论(0编辑  收藏  举报