2020-10-18 动态规划(最长回文子串)

最长回文子串

动态规划

就在这里记录一下动态规划。
动态规划的关键点就是小问题和大问题的联系。用一个递归表,更容易理解。

对于这个题,递归函数中,如果i = j,必然是回文,如果i = j-1,只需要比较这两个位置的值是否一样,否则,就需要递归比较内部的字符串。
以abccbd作为例子,最开始是f[0][0](f[i][j]指s.substring(i,j+1)这一子串是否为回文),肯定为true,接着是f01, f11, f02, f12, f22…

i \ j0a1b2c3c4b5d
0atruefalsefalsefalsefalsefalse
1btruefalsefalsetruefalse
2ctruetruefalsefalse
3ctruefalsefalse
4btruefalse
5dtrue

可以看到,当一个位置的值为true时,它还需要判断左斜下方的值是否也为true(如果存在)。比如,f14这里,先判断了b == b,接着还要判断c==c。这就是动态规划递归在表格中的体现。
最后在这个过程中会记录下最长回文出现时的i和j的值。最后根据这个值输出子串。

然后在leetcode题解里面学到了一个表达式:f = (条件a && (条件b || 条件aa)); 即,当满足条件b时,只需要满足条件a即可,否则需要满足条件a的同时也需要满足进一步的条件aa。
如果是我就会用if(b)else语句来做了。

中心扩展

动态规划在这里只是一个较差的选择,还有更优的方法。
基本思想是中心扩展法:遍历字符串,每遍历一个,就以它作为中心,然后扩散出去看是否为回文。这种解法需要先在每一个字符串中间插入一个字符串。abba ——> -a-b-b-a-。
因为题目需要输出子串,所以需要花一些心思准确定位到原字符串合适的位置上。这个不难,画图想一想就能写出代码。

Manacher算法

这里是学习了这个博客的内容,写的超级详细,很感谢。这里用自己的话写写自己的理解。

关键变量为rl数组posmaxRight
一个回文串中最左或最右位置的字符与其对称轴的距离称为回文半径,用rl[ ]记录表示,用rl[i]表示以第i个字符为对称轴的回文串的回文半径。
MaxRight,表示当前访问到的所有回文子串,所能触及的最右一个字符的位置。pos表示MaxRight对应的回文串的对称轴所在的位置。

前面一样先填充字符。首先是一次对整个字符串的遍历,需要将每个位置的回文半径值rl记录下来,并且根据其大小,对pos和maxRight进行更新。

在这个循环里面,每次迭代首先判断当前索引的位置在maxRight的左边还是右边,由此来对rl做一个初始赋值。

if(k < maxRight){
	rl[k] = Math.min(rl[2*pos-k], maxRight-k);
}
else{
	rl[k] = 1;
}

当k < maxRight时,根据回文的对称性,此时的rl[k] 值是可以参考rl[2*pos-k] 的。当然,不能超过maxRight的范围。
在此初始值的基础上,左右扩展,判断是否有更大的回文子串。
当i<maxright时的图例如下(i>=maxright时,就不能借用 j 的信息了):

	   j 	 pos  	 i	maxright
	   ↓ 	  ↓		 ↓ 		↓
  -a-b-c-b-e  -  e-b-c-b-a  -
  这个例子中,i对应的j的回文总长度就没有超过pos的半径。则不需要继续判断新的maxright。
  下面这个例子,则超过了pos的半径,所以需要扩展判断新的maxright。(编程为了方便,两者都进行判断)
	   j 	 pos  	 i	maxright
	   ↓ 	  ↓		 ↓ 		↓
  -a-b-c-b-a  -  a-b-c-b-a  -???

关键点

  1. 理解上面三个关键变量的含义和作用。
  2. 理解如何给rl[k]赋予初始值的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值