力扣647. 回文子串(中心扩展法+Manacher 算法+区间DP)

第五十七天 --- 力扣647. 回文子串(中心扩展法+Manacher 算法+区间DP)

题目要求:

在这里插入图片描述

整体思路一:区间DP

1、
<1>想DP问题的时候,在判断是否是DP以及状态转移关系的时候, 可以尝试三步走-第一-大问题是什么,第二-小问题是什么,第三-二者之间的关系。
<2>因为DP解决多段决策,且决策之间相互影响,这样三步走分析之后,就可以发现这道题有没有多段决策,并且可以看出决策之间有无关联。
<3>只要发现上述特征,就可以尝试DP,且三步走之后,转移关系也就清晰明了了。剩下的就是找到DP属于什么类型,套上该类型的大板子,找好状态,初始值,转移方程以及答案就行了。
2、分析这道题,首先判断是大区间不是回文子串,大区间是回文子串那么小区间则必定是回文子串且区间边界的两个字符必定相等(根据回文串定义),小问题就是小一点的区间上也是回文子串,那么二者关系出来了(转移方程),小区间是回文子串且边界两个字符一致即可。
3、再根据题意,由相同的字符组成但起始终点位置不一样,算作不同子串可知,我们相当于区间长度固定了,但是起点不同,典型区间DP
4、所以四个量就确定了:
<1>状态i,j表示区间端点
<2>当区间长度=0,全是回文串,当区间长度=1,区间端点的字符一样,就是回文串
<3>转移关系:小区间是回文子串且边界两个字符一致
<4>因为需要看小区间是不是回文子串,所以dp数组存储状态即是不是回文串。找到一个回文串,ans++即可。

具体代码

注:
1、DP尽量开始的时候别套模板,先分析出来是不是DP,以及是DP了怎么解,要先看大问题,再看小问题,再找二者关系(转移方程)。
2、区间DP大板子不变(枚举区间,大区间由小区间转化过来),具体的状态转移方程一定具体题意具体分析,用三步走方法看是不是DP以及转移方程到底啥样,但是状态转移方程不要一味的套模板,dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j]) + judge(i, j);这玩意针对的是一部分区间中求最优值的问题,但我们这题就是统计个数,要根据我们上面三步分析定,一定要具体问题具体分析,千万别让模板限制了自己!!!!!

class Solution {
public:
	bool dp[1000] = {};
	int num = 0;
	int ans = 0;
	int countSubstrings(string s) {
		num = s.size();
		for (int l = 0; l < num; l++) {//这里是区间DP大板子,从小到大枚举区间,大区间由小区间转移过来
			for (int i = 0, j; i + l < num; i++) {
			//l-代表区间长度,i-代表区间起点,j-代表区间终点
				j = i + l;
				if (i == j) {//初始状态
					dp[i][j] = true;
					ans++;
				}
				else if (l == 1 && s[i] == s[j]) {//初始状态
					dp[i][j] = true;
					ans++;
				}
				else if (l > 1 && dp[i + 1][j - 1] == true && s[i] == s[j]) {
					dp[i][j] = true;//转移方程
					ans++;
				}
			}
		}
		return ans;
	}
};

(所有代码均已在力扣上运行无误)

经测试,该代码运行情况是(经过多次测试所得最短时间):

在这里插入图片描述

时间复杂度:O(N^2)
空间复杂度:O(N^2)

整体思路二:中心扩展

1、我们寻找回文子串最朴素的方法就是枚举出来所有的子串,看所有的字串是不是回文子串,但是时间复杂度太高了,因为先寻找字串再判断,是O(N^3),我们不妨换个角度,每个回文串都有一个回文中心按照顺序枚举回文中心,按照该回文中心同时左右扩展,两个新的节点如果一样就接着扩展,不一样说明出现了不是回文串的部分,后面全不可能是回文串了。
2、因为回文串长度分为奇数长度和偶数长度,奇数长度和偶数长度中心位置不同,前者一个点,后者两个点,以下以n=4为例子来看一看起始位置情况:
在这里插入图片描述
说明共2*n-1种情况,li=i/2;ri=i/2+i%2。

具体代码


class Solution {
public:
	int countSubstrings(string s) {//把奇数长度和偶数长度的回文串一同考虑
		int n = s.size();
		int ans = 0;
		for (int i = 0; i < 2 * n - 1; i++) {
			int l = i / 2;
			int r = i / 2 + i % 2;
			while (l >= 0 && r < n&&s[l] == s[r]) {
				l--;
				r++;
				ans++;
			}
		}
		return ans;
	}
};

(所有代码均已在力扣上运行无误)

经测试,该代码运行情况是(经过多次测试所得最短时间):

在这里插入图片描述

时间复杂度:O(n^2)
空间复杂度:O(1)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JLU_LYM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值