不同回文子串数目 hdu 3948

将原串反向后接在后面,中间用一个没出现过的字符隔开;

如"abab";k=strlen(str);

连接后变为"abab9baba0"

根据height【】排序后

i   height[i]  子串
0     0              0
1     0              9baba0
2     0             a0
3     1             ab9baba0
4     2             aba0
5     3             abab9baba0
6     0             b9baba0
7     1             ba0
8     2             bab9baba0
9     3             baba0

奇数长度情况:

从2到n-1遍历,第i个所在位置为sa[i],和它对称的在2*k-sa[i]处,求出这2个字符串的公共前缀即为以sa[i]处为中心的回文数目。记录这个长度,如果有重复情况,那么后面求得的前缀减去它。

//不同回文子串数目
#include <iostream>
#include <string>
#include <cmath>
#include <map>
using namespace std;
const int N=210000;
int wa[N],wb[N],wv[N],wsum[N];
int height[N],sa[N],rank[N];
int n;
char str[N];
int f[N][23];
bool vis[N];
int r[N];
int ans;
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++)
		wsum[i]=0;
	for(i=0;i<n;i++)
		wsum[x[i]=r[i]]++;
	for(i=1;i<m;i++)
		wsum[i]+=wsum[i-1];
	for(i=n-1;i>=0;i--)
		sa[--wsum[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++)
			wsum[i]=0;
		for(i=0;i<n;i++)
			wsum[wv[i]]++;
		for(i=1;i<m;i++)
			wsum[i]+=wsum[i-1];
		for(i=n-1;i>=0;i--)
			sa[--wsum[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++;
	}
}
void calheight(int *r,int *sa,int n)
{
	int i,j,k=0;
	for(i=0;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 mmin(int x,int y)
{
	return x<y?x:y;
}
void rmqinit(int n)
{
	int i,j,m,k;
	m=floor(log(1.0*n)/log(2.0));
	for(i=1;i<=n;i++)
		f[i][0]=height[i];
	for(i=1;i<=m;i++)
		for(j=n;j>=1;j--)
		{
			f[j][i]=f[j][i-1];
			k=1<<(i-1);
			if(j+k<=n)
				f[j][i]=mmin(f[j][i],f[j+k][i-1]);
		}
}
int get_rmq(int x,int y)
{
	int m,t;
	x=rank[x];
	y=rank[y];
	if(x>y)  
        t=x,x=y,y=t;  
    x++;  
    m=floor(log(1.0*(y-x+1))/log(2.0));  
    return mmin(f[x][m],f[y-(1<<m)+1][m]);  
}


int main()
{
	int T,i,j,k,ca=0,s,t;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%s",str);
		k=strlen(str);
		str[k]='9';
		for(i=0;i<k;i++)
			str[i+k+1]=str[k-i-1];
		str[2*k+1]='0';
		n=2*k+2;
		str[n]='\0';
		for(i=0;i<n;i++)  
			r[i]=(int)str[i]; 
		da(r,sa,n,'z'+1);
		calheight(r,sa,n-1);
		//cout<<str<<endl;
	//	for(i=0;i<n;i++)
		//	cout<<i<<' '<<height[i]<<' '<<str+sa[i]<<endl;
		rmqinit(n-1);
		ans=0;
		memset(vis,0,sizeof(vis));
		s=0;
		for(i=2;i<n;i++)
		{
			s=mmin(s,height[i]);
			if(vis[2*k-sa[i]])
			{
				t=get_rmq(sa[i],2*k-sa[i]);
				if(t>s)
				{
					ans+=t-s;
					s=t;
				}
			}
			else
				vis[sa[i]]=1;
		}
	//	cout<<ans<<endl;
		memset(vis,0,sizeof(vis));
		s=0;
		for(i=2;i<n;i++)
		{
			s=mmin(s,height[i]);
			if(!sa[i])
				continue;
			if(vis[2*k-sa[i]+1])
			{
				t=get_rmq(sa[i],2*k-sa[i]+1);
				if(t>s)
				{
					ans+=t-s;
					s=t;
				}
			}
			else
				vis[sa[i]]=1;
		}
		printf("Case #%d: %d\n",++ca,ans);
	}
	return 0;
}




阅读更多
文章标签: ini
个人分类: 后缀数组
上一篇最长回文子串 ural1297
下一篇出现k次以上重复的最长子串 poj 3261
想对作者说点什么? 我来说一句

HDU图论题目分类

2013年04月18日 211KB 下载

动态规划背包问题入门

2011年07月07日 328KB 下载

背包问题模板 hdu2191

2014年06月24日 12KB 下载

HDU 递归题详解大全(含代码)

2011年03月31日 113KB 下载

HDU DP动态规划

2010年03月31日 454KB 下载

没有更多推荐了,返回首页

关闭
关闭