最常回文串和最长回文序列

最长回文串

两种方法

一种是使用动态规划的,这里的动态规划和最长公共子序列类似,但是计算表格的时候只要计算一般,而且是按照对角线来推进的

void subPalindrome2(char *A,char *sub)
{
	int len=strlen(A);
	int i=0,j=0,sublen;
	int maxsublen=1;
	memset(hp,0,N*N*sizeof(char));
	//初始化sublen=1,2的情况
	for(i=0;i<len;i++)
		hp[i][i]='T';
	for(i=1;i<len;i++)
	{
		if(A[i-1]==A[i])
		{
			maxsublen=2;
			hp[i-1][i]='T';
		}
		else
			hp[i-1][i]='F';
	}
	//规划sublen>2
	for(sublen=3;sublen<=len;sublen++)
	{
		for(i=0,j=sublen-1;j<len;i++,j++)
		{
			if(A[i]==A[j])
			{
				hp[i][j]=hp[i+1][j-1];
				if(hp[i+1][j-1]=='T')
					maxsublen=sublen;
			}
			else
				hp[i][j]='F';
		}
	}
	for(i=0;i<len;i++)
	{
		for(j=0;j<len;j++)
			printf("%c ",hp[i][j]);
		printf("\n");
	}
	//找出最大字串
	for(i=0,j=maxsublen-1;j<len;i++,j++)
	{
		if(hp[i][j]=='T')
			break;
	}
	for(int k=i;k<=j;k++)
		sub[k-i]=A[k];
}

第二种方法是Manacher算法

void Manacher(char *st,char* sub)
{
	//创建临时的字符串
	int len=strlen(st);
	char* T=(char*)calloc(len*2+3,sizeof(char));
	T[0]='@';T[2*len+2]='$';//防止首尾越界
	int i,maxid=0;
	for(i=1;i<=len*2;i+=2)
	{
		T[i]='#';
		T[i+1]=st[i/2];
	}
	T[2*len+1]='#';
	//建立回文半径数组(包括回文中心)
	int* R=(int*)calloc(len*2+2,sizeof(int));
	R[0]=0;
	int P=0,P0=0;
	for(i=1;i<=2*len+1;i++)
	{
		if(i<P)
			R[i]=min(P-i,R[2*P0-i]-1);
		else
			R[i]=1;
		while(T[i+R[i]]==T[i-R[i]])
			R[i]++;
		if(i+R[i]-1>P)
		{
			P=i+R[i]-1;
			P0=i;
		}
		if(R[maxid]<R[i])
			maxid=i;
	}
	//提取回文子串
	int k;
	for(k=0,i=maxid-R[maxid]+1;i<maxid+R[maxid]-1;i++)
		if(T[i]!='#')
			sub[k++]=T[i];
	sub[k]='\0';
	free(R);
	free(T);
}


Manacher python实现

#coding:UTF-8
__author__ = 'LQ'

def manacher(st):
    st='@'+st+'$'
    T='#'.join(st)#T是转换过的数组
    (P,P0,id)=(0,0,0)#P0是最远回文半径对应的中心,P是对应的最远回文半径,id是最大回文半径对应的中心,
                      #回文半径包含中心点,下标从1开始,下标i对应的回文串为[i-(R[i]-1),i+(R[i]-1)]
    R=[0]#用来保存回文半径
    for i in range(1,len(T)-1):
        if i<P:
            r=min(P-i,R[2*P0-i]-1)
        else:
            r=1
        while T[i+r]==T[i-r]:r+=1
        R.append(r)
        (P0,P)=(i,r+i-1) if r+i-1>P else (P0,P)
        id=id if R[id]>r else i
    #输出回文串
    sub=T[id-(R[id]-1):id+(R[id])]
    sub=sub.replace('#','')
    return sub



if __name__=="__main__":
    print manacher('BBABCCBCAB')


最长子序列的思想和上面字符串的思想类似,,也是动态规划

//最长回文子序列
void LPS(char *st,char* sub)
{
	memset(hp,0,N*N*sizeof(char));
	int len=strlen(st);
	int i,j;
	for(i=0;i<len;i++)
	{
		hp[i][i]=1;
		dir[i][i]='o';//表示起点
	}
	int sublen;
	//动态规划长度大于2的字串,这里注意,对于i>j的串我们已经把hp[i][j]=0,所以计算sublen=2可以统一到下面的循环
	for(sublen=2;sublen<=len;sublen++)
	{
		for(i=0;i<len-sublen+1;i++)
		{
			j=i+sublen-1;
			if(st[i]==st[j])
			{
				dir[i][j]='+';
				hp[i][j]=hp[i+1][j-1]+2;
			}
			else
			{
				if(hp[i][j-1]>hp[i+1][j])
				{
					hp[i][j]=hp[i][j-1];
					dir[i][j]='R';
				}
				else
				{
					hp[i][j]=hp[i+1][j];
					dir[i][j]='U';
				}
			}
			//这里注意起点可能也是"aa"这样的串!!!
			if(sublen==2)
				dir[i][j]='o';
		}
	}
	//for(i=0;i<len;i++)
	//{
	//	for(j=0;j<len;j++)
	//		printf("%c ",dir[i][j]);
	//	printf("\n");
	//}
	//printf("%d\n",hp[0][len-1]); 
	//回溯出最长回文串
	i=0,j=len-1;
	int pbeg=0,pend=hp[0][len-1]-1;
	while(dir[i][j]!='o')
	{
		if(dir[i][j]=='+')
		{
			sub[pbeg++]=sub[pend--]=st[i];
			i++,j--;
		}
		if(dir[i][j]=='U')i++;
		if(dir[i][j]=='R')j--;
	}
	sub[pbeg]=sub[pend]=st[i];//如果起点是一个字符pbeg=pend,如果是两个字符则不相等
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值