vjudge原地爆炸...
题意:求一个字符串不同的子串的个数
策略:后缀数组
利用后缀数组的sa和height两个功能强大的数组,我们可以实现上述操作
首先有个很显然的结论:一个字符串的所有子串=它后缀的所有前缀
这是很显然的,因为一个后缀的前缀遍历了所有以该后缀起点为起点的字符串的子串,那么如果我们遍历所有后缀的,就能找出这个字符串的所有子串了
所以对于一个起点为sa[i]的字符串,最多能提供的贡献就是l-sa[i]+1,而再考虑重复字符串的个数,也就是这个后缀所有的与其他后缀最长的公共前缀,这个后缀的贡献就是l-sa[i]+1-height[i]
然后累计即可
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> using namespace std; int sa[50005]; int rk[50005]; int height[50005]; int f1[50005]; int f2[50005]; int f3[50005]; int has[50005]; char s[50005]; int l,m=127; void init() { memset(sa,0,sizeof(sa)); memset(rk,0,sizeof(rk)); memset(has,0,sizeof(has)); memset(f1,0,sizeof(f1)); memset(f2,0,sizeof(f2)); memset(f3,0,sizeof(f3)); memset(height,0,sizeof(height)); m=127; } void turnit() { memcpy(f3,f1,sizeof(f3)); memcpy(f1,f2,sizeof(f1)); memcpy(f2,f3,sizeof(f2)); } void get_sa() { for(int i=1;i<=l;i++) { f1[i]=s[i]; has[f1[i]]++; } for(int i=2;i<=m;i++) { has[i]+=has[i-1]; } for(int i=l;i>=1;i--) { sa[has[f1[i]]--]=i; } for(int k=1;k<=l;k<<=1) { int tot=0; for(int i=l-k+1;i<=l;i++) { f2[++tot]=i; } for(int i=1;i<=l;i++) { if(sa[i]>k) { f2[++tot]=sa[i]-k; } } for(int i=1;i<=m;i++) { has[i]=0; } for(int i=1;i<=l;i++) { has[f1[i]]++; } for(int i=2;i<=m;i++) { has[i]+=has[i-1]; } for(int i=l;i>=1;i--) { sa[has[f1[f2[i]]]--]=f2[i]; f2[i]=0; } turnit(); f1[sa[1]]=1; tot=1; for(int i=2;i<=l;i++) { if(f2[sa[i]]==f2[sa[i-1]]&&f2[sa[i]+k]==f2[sa[i-1]+k]) { f1[sa[i]]=tot; }else { f1[sa[i]]=++tot; } } if(tot==l) { break; } m=tot; } for(int i=1;i<=l;i++) { rk[sa[i]]=i; } int f=0; for(int i=1;i<=l;i++) { if(rk[i]==1) { continue; } if(f) { f--; } int j=sa[rk[i]-1]; while(s[i+f]==s[j+f]) { f++; } height[rk[i]]=f; } } void solve() { int ss=0; for(int i=1;i<=l;i++) { ss-=l-sa[i]+1-height[i]; } printf("%d ",ss); } int main() { int T; scanf("%d",&T); while(T--) { init(); scanf("%s",s+1); l=strlen(s+1); get_sa(); solve(); } return 0; }