最长回文子串问题

本文探讨了四种方法来寻找给定字符串中的最长回文子串:暴力法、动态规划、中心扩展法和Manacher算法。每种方法都有详细的解释和代码实现,包括算法原理、时间复杂度和空间复杂度的分析。

题目:

给定一个字符串s,找出s中的最长回文子串;

方法:

暴力法,DP法, 中心扩展法,manacher算法

解法一:暴力法

遍历字符串S的每一个子串,去判断这个子串是不是回文,是回文的话看看长度是不是比最大的长度maxlength大。遍历每一个子串的方法要O(n^2),判断每一个子串是不是回文的时间复杂度是O(n),所以暴利方法的总时间复杂度是O(n^3)。

    //暴力法
	//对字符串的每一个子串进行判断是不是回文串,更新最长回文子串的长度值
	private static String findLongestPalindrome1(String s) {
		// TODO Auto-generated method stub
		int length = s.length();
		if(length<=1)return s;
		int maxL = 0;//最长回文子串长度
		int start = 0;//最长回文子串开始的索引值
		int index1 = 0,index2 = 0;
		for (int i = 0; i < length; i++) {
			for (int j = i+1; j < length; j++) {
				index1 = 0;//左索引
				index2 = 0;//右索引
				for (index1 = i,index2 = j; index1 < index2; index1++,index2--) {
					if(s.charAt(index1)!=s.charAt(index2)) {
						break;
					}
				}
				if(index1>=index2 && (j-i+1)>maxL) {
					start = i;
					maxL = j-i+1;
				}
			}
		}
		if(maxL>0) {
			return s.substring(start,start+maxL);
		}else {
			return s.charAt(0)+"";
		}
	}

解法二: 动态规划

回文字符串的子串也是回文,比如P[i,j](表示以i开始以j结束的子串)是回文字符串,那么P[i+1,j-1]也是回文字符串。这样最长回文子串就能分解成一系列子问题了。这样需要额外的空间O(N^2),算法复杂度也是O(N^2)。

首先定义状态方程和转移方程:

P[i,j]=false:表示子串[i,j]不是回文串。P[i,j]=true:表示子串[i,j]是回文串。

P[i,i]=true:当且仅当P[i+1,j-1] = true && (s[i]==s[j])

否则p[i,j] =false;

关于动态规划问题详见:https://www.cnblogs.com/mini-coconut/p/9075277.html(我现在还是不会写  -_-||)

//动态规划
	public static String findLongestPalindrome2(String s){
        int len = s.length();
        int start = 0;
        int maxlength = 0;
        boolean p[][] = new boolean[s.length()][s.length()];
        // 子串长度为1和为2的初始化
        for(int i = 0; i < len; i++){
            p[i][i] = true;
            if(i < len - 1 && s.charAt(i) == s.charAt(i + 1)){
                p[i][i + 1] = true;
                start = i;
                maxlength = 2;
            }
        }
        // 使用上述结果可以dp出子串长度为3~len -1的子串
        for(int strlen = 3; strlen < len; strlen ++){
            for(int i = 0; i <=len - strlen; i++){
                int j = i + strlen - 1; // 子串结束的位置
                if(p[i + 1][j - 1] && s.charAt(i) == s.charAt(j)){
                    p[i][j] = true;
                    maxlength = strlen;
                    start = i;
                }
            }
        }
        if(maxlength > 0)
            return s.substring(start, start + maxlength);
        else
        	return s.charAt(0)+"";
    }

解法三:中心扩展法

中心扩展就是把给定的字符串的每一个字母当做中心,向两边扩展,这样来找最长的子回文串。算法复杂度为O(N^2)。

但是要考虑两种情况:

1、像aba,这样长度为奇数。

2、想abba,这样长度为偶数。

//中心扩展法
	private static String findLongestPalindrome3(String s) {
		// TODO Auto-generated method stub
		int length = s.length();
		if(length<=1)return s;
		int maxL = 0;//最长子串长度
		int start = 0;//回文子串开始索引
		
		//考虑像abc类型
		for (int i = 0; i < length; i++) {
			int l = i-1;
			int r = i+1;
			while(l>=0 && r<length && s.charAt(l) == s.charAt(r)) {
				if(maxL < (r-l+1)) {
					maxL = (r-l+1);
					start = l;
				}
				r++;
				l--;
			}
		}
		//考虑abba类型
		for (int i = 0; i < length; i++) {
			int l = i;
			int r = i+1;
			while(l>=0 && r<length && s.charAt(l) == s.charAt(r)) {
				if(maxL < (r-l+1)) {
					maxL = (r-l+1);
					start = l;
				}
				r++;
				l--;
			}
		}
		
		if(maxL>0) {
			return s.substring(start,maxL+start);
		}else {
			return s.charAt(0)+"";
		}
	}

解法四:Manacher算法

Manacher法只能解决例如aba这样长度为奇数的回文串,对于abba这样的不能解决,于是就在里面添加特殊字符。我是添加了“#”,使abba变为a#b#b#a。这个算法就是利用已有回文串的对称性来计算的,具体算法复杂度为O(N)

详细规则可参考另一篇博客http://blog.sina.com.cn/s/blog_9ca3f6e70102wsb0.html

public static String findLongestPalindrome4(String s) {
        if(s == null || s.length() < 1)
            return "";
        String str = dealWithS(s);  // 处理一下s,即将给字符串s的中间加上特殊字符,这样无论对于奇数字符还是偶数字符可以做同样的处理
        int[] res = new int[str.length()];
        int R = 0; // 当前所能扩展的半径
        int C = 0; // C位置的半径为R
        int maxC= 0; // 最长的半径的位置
        res[0] = 0;
        for(int i = 1; i < str.length(); i++)
        {
            int j = 2 * C - i;  // i点的对称点
            if(j >= 0 && res[j] < R - i)  // 对称点存在且对称点的回文半径在C的回文中
            {
                res[i] = res[j];
            }
            else  // 否则,需要根据i点一点一点的计算
            {
                int k = 1;
                while(R + k < str.length() && 2 * i - R - k >= 0)
                {
                    if(str.charAt(R + k) == str.charAt(2 * i - R - k))
                        k ++;
                    else
                        break;
                }
                res[i] = R -i + k - 1;
                if(res[i] + i > R)
                {
                    R = res[i] + i;
                    C = i;
                }
            }

            maxC = res[maxC] > res[i] ? maxC : i;  // maxC保存的是回文半径最大的那个点的位置
        }
        String subStr = str.substring(maxC - res[maxC], maxC + res[maxC] + 1);
        StringBuffer sb = new StringBuffer();
        for(int i = 0; i < subStr.length(); i++)
        {
            if(subStr.charAt(i) != '#')
                sb.append(subStr.charAt(i));
        }
        return sb.toString();
    }
    public static String dealWithS(String s)  // 将原字符串进行处理
    {
        StringBuffer sb = new StringBuffer();
        sb.append("#");
        for(int i = 0; i < s.length (); i++)
        {
            sb.append(s.charAt(i));
            sb.append("#");
        }
        return sb.toString();
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值