您的位置:首页 > 其它

spoj 405 求不同子串的个数 后缀数组和高度数组的应用

2016-07-30 21:44 453 查看
传送门:spoj 405 求不同子串的个数

题目大意

求一个字符串不同子串的个数。

解题思路

后缀数组和高度数组的应用!

因为每个子串都是每个后缀的前缀,那么原问题等价于求所有后缀之间不相同的前缀的个数如果后缀按照sa[1],sa[2]….sa
这样的顺序来计算的,那么不难发现每次增加的子串的个数为n-sa[i]+1,但是其中是有height[i]个和前面字符串的前缀相同的,所以对应于每个sa[i]对整个字符串不同子串的数量贡献
n-sa[i]+1-height[i]
所以原问题就转换为了这个公式
n-sa[i]+1-height[i]
的累加。

AC代码

#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <string>
using namespace std;

const int maxn = 1111;
char s[maxn];
int sa[maxn],t1[maxn],t2[maxn],c[maxn];
int rank[maxn],height[maxn];

void getHeight(int n){
int k = 0;
for(int i=1;i<=n;i++)rank[sa[i]] = i;
for(int i=0;i<n;i++){
if(k)k--;
int j = sa[rank[i]-1];
while(s[i+k]==s[j+k])k++;
height[rank[i]] = k;
}
}
bool cmp(int *r,int a,int b,int l){
return (r[a]==r[b] && r[a+l]==r[b+l]);
}
void build_sa(int m,int n){
int i,*x=t1,*y=t2,k,p;
for( i=0;i<m;i++)c[i] = 0;
for( i=0;i<n;i++)c[x[i] = s[i]]++;
for( i=1;i<m;i++)c[i] += c[i-1];
for( i=n-1;i>=0;i--)sa[-- c[x[i]]] = i;
for(k=1,p=0;p<n;m=p,k<<=1){
p = 0;
for(i=n-k;i<n;i++)y[p++] = i;
for(i=0;i<n;i++)if(sa[i]>=k)y[p++] = sa[i]-k;
for(i=0;i<m;i++)c[i] = 0;
for(i=0;i<n;i++)c[x[y[i]]]++;
for(i=1;i<m;i++)c[i] += c[i-1];
for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]] = y[i];
swap(x,y);
p = 1; x[sa[0]] = 0;
for(i=1;i<n;i++)
x[sa[i]] = cmp(y,sa[i-1],sa[i],k)?p-1:p++;
}
getHeight(n-1);
}
int solve(int n){
int ans = n - sa[1];
for(int i=2;i<=n;i++){
ans += n-sa[i]-height[i];
}
return ans;
}
int main(){
int T;
cin>>T;
while(T--){
scanf("%s",s);
int n = strlen(s);
build_sa(255,n+1);
printf("%d\n",solve(n));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: