判断回文串,最长回文串方法

      1、判断回文串

      回文串就是从左看与从右看都是一样的字符串,例如abcba,acddca

判断一个字符串是否为回文串:如判断s=abcdcba是否为回文串,只要看s以中间位分界线,s的左右两边是否相等即可。令id为s的下标,发现s的左右两边对称的字符的下标号的和等于s.length()-1,例如                           

 

                                                          

如图 比较s[0]和s[8],s[1]和s[7] 发现下标号相加正好等于s的长度减一。

代码:

bool isHunWen(string s)
{
	int n=s.length()/2;
	
	for(int i=0;i<n;i++)
	{
		if(s[i]!=s[s.length()-1-i])
			return false;
	}
	return true;
}

2.最长回文串

    最长回文串即一个字符串的子串是回文串,这个回文串最长为多少。

    将字符串中每一个字符的左右两边加入一个字符‘#’,一般是这个,字符‘#’必须不在字符串中出现,这样就可以避免讨论了奇数长度字符串和偶数长度字符串,例如字符串abcd,加入‘#’后,#a#b#c#d#,不管原来的是奇数长度还是偶数长度,加入‘#’后都是奇数长度,在第一个‘#’前加入'#'避免边界越界。用数组p[i]表示新字符串中以str(i)字符为中心的最长字符串个数,包括str(i)这个字符,即以str(i)为起点向右移动p[i] 个字符,例如
  

                       

发现p[i]-1就是原字符在原字符串中的回文长度,于是只要求出p[]数组的各个长度就一扫就知道最长回文子串长度了

那么如何求p呢?

    引入mx表示新字符串中回文子串的最右边界,id表示这个回文子串的中心位置,p[id]表示以str[id]为起点,向右一直移动到回文子串的边界处的字符个数,包括str[id],所以mx=id+p[id],令j=2*id-i,则i与j关于id对称,如果mx>i,则p[i]=min(p[2*id-i],mx-i),即p[i]能取到最小值,为什么,借助图来理解:

             

当mx>i 时,若mx-i>p[j],则mx与i中间的距离变大,以str[j]为中心的回文子串包含在以id为中心的回文字串中,i与j对称,以i为中心的回文子串也包含在以id为中心的回文子串中,,i与j都在向id靠近,p[i]=p[j],若mx-i<=p[i],则i与mx之间距离在变小,以j为中心的回文子串不一定包括在以id为中心的回文子串中,p[i]=mx-i;当mx<=i时候,就一个字符,

p[i]=1。

            

             这个算法就叫做manacher算法,时间复杂度O(n),因为是线性的,p数组不断的向后线性移动          

  代码如下:

#include<iostream>
//#include<cstring>
#include<vector>
//#include<algorithm>
using namespace std;

const int N=3000000;
int n;
char s[N],str[N];
int p[N];
inline void checkMax(int& ans,int b)
{
	if(ans<b)
		ans=b;
}
inline int  min(int a,int b)
{
	if(a>b) 
		return b;
	else
		return a;
}
//字符串的匹配,逐步向后移动
void pipei()
{
	int mx=0,i,id;
	for(i=0;i<n;i++)
	{
		if(mx>i)
			p[i]=min(p[2*id-i],id+p[id]-i);//以id为中心
		else
			p[i]=1;
		for(;str[i+p[i]]==str[i-p[i]];p[i]++)//以i为中心,找到i为中心的回文串,左右两边匹配
			;
		if(i+p[i]>mx)                       //以i为中心的
		{
			mx=i+p[i];
			id=i;
		}
	}
}
void init()
{
	int i,j,k;
	n=strlen(s);
	str[0]='$';
	str[1]='#';
	for(i=0;i<n;i++)           
	{
		str[i*2+2]=s[i];
		str[i*2+3]='#';                 
	}
	n=n*2+2;
	str[n]=0;                  //防止边界越界
}
void pt()
{
	int ans=0,i;
	/*for(i=0;i<n;i++)
		cout<<str[i];
	puts("");
	for(i=0;i<n;i++)
		cout<<p[i];
	puts("");*/
	for(i=0;i<n;i++)
		checkMax(ans,p[i]);
	cout<<"最长回文串:"<<ans-1<<endl;
}

int main()
{
	while(scanf("%s",s)!=EOF)
	{
		init();
		pipei();
		pt();
	}
	return 0;
}



求最长回文串的方法英文文档如:http://leetcode.com/2011/11/longest-palindromic-substring-part-ii.html

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值