码迷,mamicode.com
首页 > 编程语言 > 详细

最长回文子串(Manacher算法模板题)&&对称字符串问题

时间:2016-08-29 15:38:32      阅读:535      评论:0      收藏:0      [点我收藏+]

标签:

manacher:可以解决最长回文问题。

算法:1.首先,将字符串的每个字符左右加入#,并在s0位置加入*(如果字符串中本身含有这些,则换成未出现过的字符),此时字符串的长度为len+len+3,即加入了len+1个#和一个*; (比如:aba变成 *#a#b#a#)

   2.得到一个p数组,该数组是基于新字符串进行的。

       得到p数组 :①从1~2*len遍历字符串,即从第一个#到最后一个字符(或者说*和最后一个#不用),即要得到p[1]~p[2*len];

             ②遍历的过程中,定义了两个新变量,id和mx,id标记的是,某个字符的位置;mx标记的是,能够匹配到了的最远的位置,而开始匹配的点为             id.换句话说:当处于id这个位置匹配回文的时候,mx到达了当前最远的位置,详细作用见代码。

             ③p数组的含义:达到某一个字符的时候,我们肯定要知道该字符能够与周围构成多长的回文,比如 s#b#a#b#t,中间哪个a,能够与#b#构成回            文,所以a这个位置的p值就为4,也就是说,p值代表的就是i这个位置的字符能够给出多长的回文串。

             ④匹配方法:我们可以看出,任何一个字符的p都至少为1(自己跟自己回文),那么我们只要当s[--i]==s[++j]的时候,让p++就可以了,知道两者不想等。但是,这样时间复杂度就高了很多,所以,我们要利用前面求的的p数组来减少不必要的匹配。

             ⑤减少匹配次数:利用mx和id以及对称性,具体看代码。

下面计算P[i],该算法增加两个辅助变量id和mx,其中id表示最大回文子串中心的位置,mx则为id+P[id],也就是最大回文子串的边界。

这个算法的关键点就在这里了:如果mx > i,那么P[i] >= MIN(P[2 * id - i], mx - i)。

具体代码如下:

技术分享
if(mx > i)
{
      p[i] = (p[2*id - i] < (mx - i) ? p[2*id - i] : (mx - i));
}
else
{
       p[i] = 1;
}
技术分享

当 mx - i > P[j] 的时候,以S[j]为中心的回文子串包含在以S[id]为中心的回文子串中,由于 i 和 j 对称,以S[i]为中心的回文子串必然包含在以S[id]为中心的回文子串中,所以必有 P[i] = P[j],见下图。

技术分享

当 P[j] > mx - i 的时候,以S[j]为中心的回文子串不完全包含于以S[id]为中心的回文子串中,但是基于对称性可知,下图中两个绿框所包围的部分是相同的,也就是说以S[i]为中心的回文子串,其向右至少会扩张到mx的位置,也就是说 P[i] >= mx - i。至于mx之后的部分是否对称,就只能一个一个匹配了。

技术分享

对于 mx <= i 的情况,无法对 P[i]做更多的假设,只能P[i] = 1,然后再去匹配了



代码如下:
 


 1 void manacher() //manacher 函数 
 2 {  
 3     int len=strlen(s);  
 4     for(int i=len;i>=0;--i) //将s扩大,中间加#,开头加* 
 5     {  
 6         s[i+i+2]=s[i];  
 7         s[i+i+1]='#';  
 8     }  
 9     s[0]='*';  
10     int id,mx=0;  //mx代表以id为中心时,到达最远的位置 
11     for(int i=1;i<len+len+1;++i)
12     {  
13         if(mx>i) p[i]=min(p[2*id-i],mx-i); //如果到达最远位置大于当前匹配的地方,则p[i]取min(id的对称点的p,到达最远距离-i) 
14         else p[i]=1; //如果i在mx右方,则p[i]=-1; 
15         while(s[i-p[i]] == s[i+p[i]])++p[i];   //判断i回文长度 
16         if(i+p[i]>mx) //看是否要更新最远距离,如果要,将此点作为中心。 
17         {
18             id=i;
19             mx=p[i]+i;
20         }
21     }  
22 }


对称字符串问题

计算给定字符串的最常对称子串的长度,例如“iqiyi”中的最长对称子串为“i”,“iqiyiyiq”的最长对称子串为“qiyi”和"qyiq",长度为4,给定字符串为纯小写字母的组合。

输入

输入数据为单行字符串,只含有小写字母,中间无空格。

输出

最长对称字符串的长度。

样例输入

iqiyiyiq

abccba

样例输出

4

3

#include<iostream>    
#include<string>       
using namespace std;
int main()
{
	string s;
	while (getline(cin,s))
	{
		string s1 = "*#";
		for (int i = 0; i < s.size(); i++)
		{
			s1 += s[i];
			s1 += '#';
		}
		int *p = new int[s1.size()];
		for (int i = 0; i<s1.size(); i++)
			p[i] = 1;
		int id = 0;
		int mx = 0;
		for (int i = 1; i < s1.size(); i ++)
		{
			if (mx>i)p[i] = (p[2*id-i]>mx-i?mx-i:p[2*id-i]);
			else p[i] = 1;
			while (s1[i - p[i]] == s1[i + p[i]])
				p[i]++;
			if (i + p[i] > mx)
			{
				id = i;
				mx = p[i] + i;
			}
		}
		
		int max = 0;
		for (int i = 1; i < s1.size(); i ++)
		{
			if (p[i]>max)
			max = p[i];
		}
		if ((max-1)%2==0)
		cout <<( max - 1 )/2<< endl;
		else cout << (max-1)/2+1<< endl;
	}
	return 0;
}





1、http://acm.hdu.edu.cn/showproblem.php?pid=3068

超时错在用了好几次strlen(),改正方法int n=strlen();再次调用时用常量就可以不超时

2、题目大意:

给定一个字符串,只含有小写字母,求最长回文子串的长度,最简单的求回文子串的题目,

最长回文

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4275    Accepted Submission(s): 1417


Problem Description
给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.
回文就是正反读都是一样的字符串,如aba, abba等


 

Input
输入有多组case,不超过120组,每组输入为一行小写英文字符a,b,c...y,z组成的字符串S
两组case之间由空行隔开(该空行不用处理)
字符串长度len <= 110000


 

Output
每一行一个整数x,对应一组case,表示该组case的字符串中所包含的最长回文长度.


 

Sample Input
aaaa abab


 

Sample Output
4

3

#include<stdio.h>  
#include<string.h>  
#include<algorithm>  
using namespace std;  
#define maxx 20000050  
char str[2*maxx];  
char s[maxx];  
int p[maxx];  
void Manacher(int *p,char *str,int len)  
{  
    int mx=0;  
    int idx=0;  
    for(int i=1; i<len; i++)  
    {  
        p[i]=mx>i?min(p[2*idx-i],mx-i):1;  
        while(str[i+p[i]]==str[i-p[i]])  
        p[i]++;  
        if(i+p[i]>mx)  
        {  
            mx=i+p[i];  
            idx=i;  
        }  
    }  
}  
int main()  
{  
    while(scanf("%s",s)!=EOF)  
    {  
        int nn=strlen(s);//需要定义一个变量nn,如果每次调用strlen(s),时间就会长  
        int n=2*nn+2;  
        str[0]='$';  
        for(int i=0; i<=nn; i++)  
        {  
            str[2*i+1]='#';  
            str[2*i+2]=s[i];  
        }  
        Manacher(p,str,n);  
        int ans=1;  
        for(int i=0; i<n; i++)  
                    ans=max(ans,p[i]);  
        printf("%d\n",ans-1);  
  
    }  
    return 0;  
}  
/* 
aaaa 
 
abab 
*/  

最长回文子串(Manacher算法模板题)&&对称字符串问题

标签:

原文地址:http://blog.csdn.net/asd199086/article/details/52269320

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!