您的位置:首页 > 其它

Hdu 4552 怪盗基德的挑战书 (所有前缀出现次数和 后缀数组或KMP)

2014-07-06 20:21 363 查看
题意:所有前缀出现次数和

之前用kmp写过类似的题,这次尝试用后缀数组写一下。

求出每个后缀和最长的后缀的公共前缀长度就可以了,最长的后缀是字符串本身,也就是rank[0],所以在Height数组中从rank[0]的位置往两边找即可

后面附两份KMP的。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))

const int N = int(1e5)+10;

int cmp(int *r,int a,int b,int l){
	return (r[a]==r[b]) && (r[a+l]==r[b+l]);
}
int wa
,wb
,ws
,wv
;
int rank
,height
,sa
;

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];
		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++;
		//printf("p = %d\n", p );
	}
}
void calheight(int *r,int *sa,int n){
//	memset(height,0,sizeof(height));
//	memset(rank,0,sizeof(rank));
	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++);
}

char str
;
int data
;

int main ()
{
	while (~scanf("%s",str))
	{
		int n=strlen(str);
		for (int i=0;i<=n;i++)
			data[i]=str[i];
		DA(data,sa,n+1,128);
		calheight(data,sa,n);
		int ans=n;
		int t=rank[0];
		int tmp=n;
		while (t<n)
		{
			tmp=min(tmp,height[t+1]);
			t++;
			ans+=tmp;
		}
		t=rank[0];
		tmp=n;
		while(t>1)
		{
			tmp=min(tmp,height[t]);
			t--;
			ans+=tmp;
		}
		printf("%d\n",ans%256);
	}
	return 0;
}


#include <cstdio>
#include <cstring>

const int N=100005;

char str
;
int next
,cnt
,n,sum;

void getNext (char s[],int len)
{
    next[0]=-1;
    int i=0,j=-1;
    while (i<len)
    {
        if (j==-1 || s[i]==s[j])
            next[++i]=++j;
        else j=next[j];
    }
}

void KMP ()
{
    int i=0,j=0;
    while (i<n)
        if (j == -1 || str[i] == str[j])
            i++,j++,cnt[j]++;
        else
            j=next[j];

    for (i=n;i>0;i--) //统计每个前缀出现次数,cnt[i]表示长度为i的前缀出现了cnt[i]次
        if (next[i] != -1)
		{
            cnt[next[i]] =(cnt[next[i]]+cnt[i])%256;
			sum=(sum+cnt[i])%256;
		}
}

int main ()
{
	while (~scanf("%s",str))
	{
		sum=0;
		n=strlen(str);
		memset(cnt,0,sizeof(cnt));
		getNext(str,n);
		KMP();	
		printf("%d\n",sum);
	}
    return 0;
}


#include <cstdio>
#include <cstring>

const int MOD=256;
const int N=100005;
char s
;
int next
,n,f
;

void getNext(char s[],int len)
{
    next[0]=-1;
    int i=0,j=-1;
    while (i<len)
    {
        if (j==-1 || s[i]==s[j])
            next[++i]=++j;
        else j=next[j];
    }
}

int DP ()
{
    int ans=0;
    memset(f,0,sizeof(f));
    for (int i=1;i<=n;i++)
    {//f[i]表示长度为i的字符串(也就是把str[i]当成'\0')总共含前缀的数量
        f[i]=f[next[i]]+1;//+1表示长度为next[i]的前缀在当前结尾又出现了一次
        ans=(ans+f[i])%MOD;
    }
    return ans;
}

int main ()
{
	while (~scanf("%s",s))
	{
		n=strlen(s);
		getNext(s,n);
		printf("%d\n",DP());
	}
	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: