最长回文子串两种解法

public String longestPalindrome(String s) {

		// 思路:动态规划,dp[i][j]表示s[i~j]是否为回文串,当i==j的时候,只有一个字符,显然是回文,也就是dp[i][i]=true
		// 当i+1=j的时候,只有s[i]=s[j]他才是回文串,除这两种情况外,dp[i][j]要是true的话,必须dp[i+1][j-1]为true,且s[i]=s[j]
		// 例如abbcbba,当i=0,j=6的是,要判断s[0~6]是否是回文串,先判断s[1~5]是否是回文,再看s[0]是否等于s[6]
		if (s.length() <= 1)
			return s;
		boolean[][] dp = new boolean[s.length()][s.length()];
		// 使用三个值来保存最长的那个回文子串位置
		int left = 0;
		int right = 0;
		int max = 1;
		for (int j = 1; j < s.length(); j++) {
			dp[j][j] = true;
			for (int i = 0; i < j; i++) {
				//
				if (i + 1 == j)
					dp[i][j] = (s.charAt(i) == s.charAt(j));
				else
					dp[i][j] = (s.charAt(i) == s.charAt(j) && dp[i + 1][j - 1]);

				if (dp[i][j] && j - i + 1 > max) {
					max = j - i + 1;
					left = i;
					right = j;
				}
			}

		}
		return s.substring(left, right + 1);
	}

另一个解法:马拉车算法,时间复杂度只有O(n)

// 马拉车算法
	public String longestPalindrome(String s) {

		if (s.length() <= 1)
			return s;
		// 预处理,在每个字符的前后都加一个特殊字符#
		StringBuilder sb = new StringBuilder();
		for (char c : s.toCharArray()) {
			sb.append("#");
			sb.append(c);
		}
		sb.append("#");
		// 若输入的s为aabba,那么警告处理后s变为#a#a#b#b#a#,这样做的目的是将其变为奇数长
		s = sb.toString();
		// 设置数组len,len[i]表示以s[i]为中心的回文串折叠起来的长度,比如#a#b#a#,那么len[3] =
		// 4,因为#a#b#a#折叠起来是#a#b,长度为4
		// len有一个性质,那就是len[i]-1表示s在未预处理之前的以i为中心的回文串长度,证明如下:经过预处理之后的s中,#总比字符个数多一个,那么假设
		// 字符个数为x,那么#就为x+1,那么x+x+1=2*Len[i]-1,(2*len[i]-1是把折叠的展开),可得x=len[i]-1
		int[] len = new int[s.length()];
		len[0] = 1;

		// 此外还要定义2个值,保存最长回文子串的位置信息,center表示最长回文串的中心,right表示最长回文子串的右边界
		int center = 0;
		int right = 0;

		for (int i = 1; i < s.length(); i++) {
			char c = s.charAt(i);
			boolean flag = true; // flag表示是否需要重新求
			if (i < right) {      //当前位置i若在最长回文字符串右边界覆盖范围之内
				int left_mirror = 2 * center - i;     //求i关于center的对称点,因为对称点的len我们已经求出来了,可以利用
				//如果i加上len[left_mirror]超过了最右边界,说明i这个位置的回文串需要重新计算
				if (i + len[left_mirror] > right) {   
					len[i] = right - i + 1;
					
				} else {   //否则,i加上len[left_mirror]超不过右边界,就可以用它的对称点的len值,他们是相等的
					len[i] = len[left_mirror];
					flag = false;

				}
			}
			//需要重新计算的话,就是从i向两边发散
			if (flag) {
				while (i - len[i] >= 0 && i + len[i] < s.length() && s.charAt(i - len[i]) == s.charAt(i + len[i]))
					len[i]++;

				// 更新最长的回文串对应的中心
				while (len[i] > right - center + 1) {
					center = i;
					right = center + len[i] - 1;
				}
			}
		}
		// 输出结果
		StringBuilder res = new StringBuilder();
		for (int i = center - len[center] + 2; i <= center + len[center] - 1; i += 2)
			res.append(s.charAt(i));
		return res.toString();

	}

 

做个记录!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值