现在的位置: 首页 > 综合 > 正文

hdu3518

2013年06月06日 ⁄ 综合 ⁄ 共 2003字 ⁄ 字号 评论关闭

重复至少俩次的不重叠子串的个数

我还是觉得大牛的这句解释十分重要,还是没彻底理解透,导致今天这道题目的悲剧

若在假设重复子串的长度最多为L的限制下有解, 则对于任意一个比L小的限制L'<L, 也一定有解. 这就说明存在解的连续性

  给出一个关于LCP的定理LCP(SA[i], SA[j]) = RMQ(Height[i+1..j]). 由此, 若存在k, 满足Height[k] < L, 则对于所有i, j 满足i < k < j, 有LCP(SA[i], SA[j]) < L. 即公共长度至少为L的两个后缀, 不会跨过一个小于L的Height低谷k, 所以我们可以得到一些由这些低谷划分开的连续的.

枚举字串长度h

对于每一次的h,利用height数组,找出连续的height大于等于h的里面最左端和最右端得为之l和r。

如果l + h - 1 < r的话,说明没有重叠,答案加1.

原来是这样,我一直不能理解,就是为什么是找出连续的height大于等于h,现在稍微有些体会了,在同一连续height大于等于h的区间中,则公共前缀至少为h,这样也不用担心重复计数了,其他长度为h的重复子串则会出现在另一个连续的大于等于h的height

郁闷了那么久…………现在要学会熟练的应用模板啦

这个模板还不错,还挺快的

#include <stdio.h>   
#include <stdlib.h>   
#include <string.h>   
  
int const N= 1010;   
int wa[N], wb[N], ws[N], wv[N];   
int sa[N], height[N], rank[N],r[N];   
char str[N];
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 ) ws[i]= 0;   
    for( i= 0; i< n; ++i ) ws[ x[i]= r[i] ]++;   
    for( i= 1; i< m; ++i ) ws[i]+= ws[i-1];   
    for( i= n- 1; i>= 0; i-- ) sa[ --ws[ 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 ) ws[i]= 0;   
        for( i= 0; i< n; ++i ) ws[ wv[i] ]++;   
        for( i= 1; i< m; ++i ) ws[i]+= ws[i-1];   
        for( i= n- 1; i>= 0; i-- ) sa[ --ws[ wv[i] ] ]= y[i];   
  
        t= x, x= y, y= t, p= 1; x[ sa[0] ]= 0;   
        for( i= 1; i< n; ++i )   
            x[ sa[i] ]= cmp( y, sa[i-1], sa[i], j )? p- 1: p++;   
    }   
}   
  
void callheight( 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; height[ rank[i++] ]= k )   
        for( k?k--:0, j= sa[ rank[i]- 1]; r[i+k]== r[j+k]; k++ );   
}//上面都是模板来的
int main()
{
	int ans,i,len;
	while(gets(str))
	{
		if(str[0]=='#')
			break;
		len=strlen(str);
		for(i=0;i<len;i++)
			r[i]=(int)str[i];
		r[len]=0;//这个赋值为0也是关键所在
		da(r,sa,len+1,256);
		callheight(r,sa,len);
		int ans = 0, minid, maxid;
        for(i = 1; i <=( len+1)/2; i++)//查一半就好了,长度大于(len+1)/2的子串不可能重复俩次啦
        {
            minid = 1200, maxid = -1;
            for(int j = 1; j <len+1; j++)
			{
                if (height[j] >= i)
                {
                    if (sa[j - 1] < minid) minid = sa[j - 1];
                    if (sa[j - 1] > maxid) maxid = sa[j - 1];
                    if (sa[j] < minid) minid = sa[j];
                    if (sa[j] > maxid) maxid = sa[j];
                }
                else
                {
                    if (maxid != -1 && minid + i <= maxid) ans++;
                    minid = 1010, maxid = -1;
                }
			}
            if (maxid != -1 && minid + i <= maxid) ans++;// 这里还要一步判断,因为可能还有一个连续的大于等于h的height还没判断
        }
        printf("%d\n", ans);
    }
	return 0;
}

抱歉!评论已关闭.