最长回文字符串

第一次接触回文字符串是在一次一个游戏公司的笔试中,当时用的是后缀数组来处理的,应该是写错了。其实利用后缀数组是可以求最长回文字符串的,需要一个简单处理先。

1、后缀数组实现

具体最法是:将整个字符串反过来写在原字符串的后面,中间用一个特殊的字符隔开(该字符必须是最小的)。该问题则转化为求新字符串的某两个后缀的最长公共前缀。

例如字符串s="aabaaaab",则:

新字符串可表示为ss="aabaaaab$baaaabaa"。接下来,直接求ss某两个后缀的最长公共前缀。

这个做法的时间复杂度为O(nlongn)。但在leetcode测试提示Time Limit Exceeded。

class Solution {
public:
    struct {
    	bool operator()(const char *s1, const char *s2)
    	{
    		if (strcmp(s1, s2) < 0)
    		{
    			return 1;
    		}
    		return 0;
    	}
    } customLess;

    int comlen(char *s1, char * s2)
    {
    	int i = 0;
    	for (int k = 0; k < strlen(s1)&&k < strlen(s2); k++)
    	{
    		if (s1[k] == s2[k])
    			++i;
    		else
    			break;
    	}
    	return i;
    }
    string longestPalindrome(string s) {
    	if (s.length() == 0|| s.length() == 1)
    		return s;
    	
    	string r_s;//reorder string
    	for(int i = s.length()-1; i >= 0; i--)
    		r_s += s[i];
    	
    	string s_r_s = s+" "+r_s;
    	
    	vector<char *> ptr;
    	for(int i = 0; i < s_r_s.length(); i++)
    		ptr.push_back(&s_r_s[i]);
    
    	sort(ptr.begin(), ptr.end(),  customLess);
    	
    	int maxlen = 0, maxi = 0;
    	for (int i = 0; i < (ptr.size() - 1); i++)
    	{
    		int temp = comlen(ptr[i], ptr[i+1]);
    		if (temp > maxlen)
    		{
    			maxlen = temp;
    			maxi = i;
    		}
    	}
    	
    	string ss(ptr[maxi]);
    	ss=ss.substr(0,maxlen);
    	return ss;
    }
};
在leetcode上写的程序,有人用这种方法通过,希望能交流交流。

2、Manacher算法实现

该方法能够以O(n)时间完成。算法大致过程:

在每两个相邻的字符串中插入一个原串中未出现过的字符串作为分隔符。这样整个串的长度为奇数,避免了需要区分奇数和偶数长度的考虑。

添加一个辅助数组P记录以每个字符为中心的最长回文串的长度,即p[i]记录的是以字符串s[i]为中心的最长回文串的长度 。

为了防止字符比较时候的越界,在串头还增加了一个特殊字符(因此,对新串的处理都是下标1开始的)。

如:原串s=“waabwswfd",修改后为s="$#w#a#a#b#w#s#w#f#d#"。

此串对应的辅助数组p为:1212321212141212121,则p[maxId]-1就是该串的最长回文串的长度。

数组P的具体求解过程如下图所示:

其中,id表示最大回文子串中心的位置,mx则为id+P[id],也就是最大回文子串的边界。

p[i]的求解过程代码表示为:

if(mx > i)
                p[i] = min(p[2*id - i], mx - i);
            else
                p[i] = 1;
            for(; s[i - p[i]] == s[i + p[i]]; p[i]++);

下面是整个求解最长回文字符串的代码,leetcode上已经accepted了 。

class Solution {
public:
    void initString(string& s)
    {
        string s_temp = "$#";
        for(int i = 0; i < s.length(); i++)
        {
            s_temp += s[i];
            s_temp += '#';
        }
        s = s_temp;
    }
    void computingP(int* p, int len, string s)
    {
        int mx = 0;
        int id = 0;
        for(int i = 1; i < len; i++)
        {
            if(mx > i)
                p[i] = min(p[2*id - i], mx - i);
            else
                p[i] = 1;
            for(; s[i - p[i]] == s[i + p[i]]; p[i]++);
            if(i + p[i] > mx)
            {
                mx = i + p[i];
                id = i;
            }
        }
    }
    string longestPalindrome(string s) {
    	if(s.length() == 0 || s.length() == 1)
    	   return s;
    	initString(s);
    	int len = s.length();
    	int* p = new int[len];
    	memset(p, 0, len*sizeof(int));
    	computingP(p, len, s);
    	int maxP = p[0];
    	int maxId = 0;
    	for(int i = 1; i < len; i++)
    	    if(p[i] > maxP)
    	    {
    	        maxP = p[i];
    	        maxId = i;
    	    }
    	    
    	string subStr;
    	int num = 0;
    	for(int i = 0; ; i++)
    	{
    	    if(s[maxId - maxP + 1 + i] != '#')
    	    {
    	        subStr += s[maxId - maxP + 1 + i];
    	        num++;
    	    }
    	    if(num >= (maxP - 1))
    	        break;
    	}
    	
    	return subStr;
    	        
    }
};

参考 http://blog.163.com/zhaohai_1988/blog/static/2095100852012716105847112/,而且这篇文章上还有其他的求解方法。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值