HDU3068/Leetcode5 最长回文 (Manacher算法)

10 篇文章 0 订阅

题目来源:

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

https://leetcode.com/problems/longest-palindromic-substring/


问题描述:

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


分析:

若直接使用对某个位置向两边搜索的方法leetcode能通过;

而hdoj中“字符串长度len <= 110000”,会超时!看来只能用Manacher算法。

这个算法可以看:http://blog.csdn.net/yzl_rex/article/details/7908259 (其中输出结果那边的代码是错的)

关键点为下图,即求i处时可以利用关于id对称的j处的值。



leetcode上关于此题给出了几种解法:https://leetcode.com/articles/longest-palindromic-substring/

Approach #1 (Longest Common Substring) [Accepted]
Approach #2 (Brute Force) [Time Limit Exceeded]
Approach #3 (Dynamic Programming) [Accepted]
Approach #4 (Expand Around Center) [Accepted]
Approach #5 (Manacher's Algorithm) [Accepted]

尤其要注意Approach #1,求和反串的LCS:

Let’s try another example: S=''abacdfgdcaba'', S′=''abacdgfdcaba''.
The longest common substring between S and S​​  is ''abacd''. Clearly, this is not a valid palindrome.


代码:

对某个位置向两边搜索的方法(leetcode上通过)

class Solution {
public:
    string longestPalindrome(string s) {
		char ns[102400];
		int ans[102400];
		int i,j,k;
		int slen=s.length();
		//方便偶数长时处理
		for(i=0;i<slen;i++)
			ns[2*i]='#',ns[2*i+1]=s[i];
		ns[2*i]='#';

		for(i=0;i<=2*slen;i++){
			ans[i]=0;
			for(j=1;i-j>=0 && i+j<=2*slen;j++){ //ns开头若再补上个‘*’,配合结尾的‘\0’,则不用判断i,j越界
				if(ns[i-j]==ns[i+j])
					ans[i]++;
				else
					break;
			}
		}

		int idx=0;
		for(i=0;i<=2*slen;i++)
			if(ans[idx]<ans[i]){
				idx=i;
			}

		int l=ans[idx];
		string as="";
		for(i=idx-l+1;i<=idx+l-1;i+=2)
			as+=ns[i];
		return as;
    }
};

Manacher算法(HDOJ通过)时间复杂度O(n)

#include <iostream>
using namespace std;
#include <string>
const int SZ=110000;

char ns[2*SZ+5];
int ans[2*SZ+5];

int main()
{
	int i;
	string s;
	while(cin>>s){
		int slen=s.length();
		for(i=0;i<slen;i++)
			ns[2*i+1]='#',ns[2*i+2]=s[i];
		ns[0]='*';
		ns[2*i+1]='#';

		int mans=0,id,mx=0;
		for(i=0;i<=2*slen+1;i++){
			if(mx>i)
				ans[i]=min(mx-i,ans[2*id-i]);
			else
				ans[i]=1;
			while(ns[i-ans[i]]==ns[i+ans[i]])
				ans[i]++; //因为#的存在所以只加1
			if(i+ans[i]>mx){
				mx=i+ans[i];
				id=i;
			}
			if(mans<ans[i])
				mans=ans[i];
		}
		cout<<mans-1<<endl;
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值