hdu 3518 ( 后缀数组 至少出现两次的不重叠子串个数)

来源:互联网 发布:qstring转char数组 编辑:程序博客网 时间:2024/06/06 04:49

题意 : 一个字符串,找出这个字符串中至少出现两次的子串个数。( 至少出现两次指不能出现重叠 )

思路 : 因为字符串的长度只有1000,所以O( n * n ) 暴力统计也吃得消。做法是枚举子串的长度L ,然后根据这个L给height数组分组统计就可以了。


#include <stdio.h>  #include <string.h>#include <string>#include <vector>#include <iostream>#include <algorithm>  using namespace std;  #define maxn 1005int wa[maxn],wb[maxn],wv[maxn],wt[maxn];  typedef long long LL ;int cmp(int *r,int a,int b,int l)  {return r[a]==r[b]&&r[a+l]==r[b+l];}  void da(int *r,int *sa,int n,int m){  int i,j,p,*x=wa,*y=wb,*t;  for(i=0;i<m;i++) wt[i]=0;  for(i=0;i<n;i++) wt[x[i]=r[i]]++;  for(i=1;i<m;i++) wt[i]+=wt[i-1];  for(i=n-1;i>=0;i--) sa[--wt[x[i]]]=i;  for(j=1,p=1;p<n;j*=2,m=p){  for(p=0,i=n-j;i<n;i++) y[p++]=i;  for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;  for(i=0;i<n;i++) wv[i]=x[y[i]];  for(i=0;i<m;i++) wt[i]=0;  for(i=0;i<n;i++) wt[wv[i]]++;  for(i=1;i<m;i++) wt[i]+=wt[i-1];  for(i=n-1;i>=0;i--) sa[--wt[wv[i]]]=y[i];  for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)  x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;  }  }  int Rank[maxn],height[maxn];  void calheight(int *r,int *sa,int n){  int i , j , k = 0 ;  for( i=1 ; i<=n ; i++ ) Rank[sa[i]]=i;  for(i=0;i<n;i++) {  if(k)k--;  int j = sa[Rank[i]-1];  while(r[i+k]==r[j+k]) k++ ;  height[Rank[i]] = k ;  }     return;  }  int r[maxn] , sa[maxn] ;  char str[maxn] ;int main(){while( scanf( "%s" , str ) != EOF ) {if( strcmp( str , "#" ) == 0 ) break;int ans = 0 ;int len = strlen( str ) ;for( int i = 0 ; i < len ; i ++ ) r[i] = str[i] ; r[len] = 0 ;da( r , sa , len + 1 , 200 ) ;calheight( r , sa , len ) ;for( int L = 1 ; L <= len / 2 ; L ++ ) {int Max = sa[1] ;int Min = sa[1] ;for( int i = 2 ; i <= len ; i ++ ) {if( height[i] < L ) {if( Max - Min >= L ) ans ++ ;Max = Min = sa[i] ;}Max = max( Max , sa[i] ) ;Min = min( Min , sa[i] ) ;}if( Max - Min >= L ) ans ++ ;}printf( "%d\n" , ans ) ;}return 0 ;}


0 0
原创粉丝点击