马拉车算法

题意:找给定字符串最长的回文子串

解题:马拉车算法提供了解决该题的线性方法。主要思想:从左到右依次遍历每个字符以及每个空位(因为回文字符串即有双数又有单数),对于每个位置,拓展寻找最长字串。每找一次回文字符串,如果大于设定的右边界,就拓展右边界,如果在右边界所属字串中心位置和右边界范围内,它必定与p[2*id - i]相同(即一个回文字符串右部分与左部分是对称的,我前面已经解决了左部分,现在解决右部分就可以直接在左部分基础上进行,但还有一种例外,就是左部分也是回文字符串,且它的半径延申到所在字符串的左边界以外,那么我的右部分就不能直接等于左串的值,因为还有左右边界的限定)。

解决如何遍历空隙的问题:我们在字符串每个字符两边都插入一个相同的字符,代替空格。比如123,就变成#1#2#3#

解决字符串增长后如何返回子串的问题:我们要在原来未更改的字符串中返回字串,假设原来字符串是s,改变后的字符串是sb,显然我找到sb中最长字串的半径,减一后就是s中最长字串的长度,比如#2#2#1#2#2#半径为6,22122长度为5。长度找到了,还要找到起始位置,比如在#1#2#2#1#2#2#回文子串中心的位置为7,子串长度6,7-6=1,122122中起始位置为1,正确,#b#o#b#中心位置3,半径为4,3-4=-1,错误。。。那么这个问题怎么解决?我们可以在最前面再添加一个字符$(验证略),这样就没问题了。

核心代码:p[i] = mx > i ? Math.min(p[2*id-i], mx-i) : 1;

public class Main{
	public static String findstr(String s) {
		StringBuilder sb = new StringBuilder();
		sb.append("$#");
		for (int i = 0; i < s.length(); i++) {
			sb.append(s.charAt(i));
			sb.append("#");
		}
		int[] p = new int[sb.length()];
		//mx右边界,id右边界主人中心,resLen最长子串长度,resCenter最长子串中心
		int mx = 0, id = 0, resLen = 0, resCenter = 0;
		for (int i = 1; i < sb.length(); i++) {
			p[i] = mx > i ? Math.min(p[2*id-i], mx-i) : 1;
			while (i+p[i]<sb.length() && sb.charAt(i + p[i]) == sb.charAt(i - p[i])) ++p[i];
			if (mx < i + p[i]) {
				mx = i + p[i];
				id = i;
			}
			if (resLen < p[i]) {
				resLen = p[i];
				resCenter = i;
			}
			
		}
		return s.substring((resCenter-resLen)/2, (resCenter-resLen)/2+resLen-1);
		
	}
	public static void main(String[] args){
		String s1 = "123321123456543";
		System.out.println(findstr(s1));
	}
	
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值