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; }