spoj New Distinct Substrings
阿新 • • 發佈:2018-12-12
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\n",ss); } int main() { int T; scanf("%d",&T); while(T--) { init(); scanf("%s",s+1); l=strlen(s+1); get_sa(); solve(); } return 0; }