力扣647. 回文子串(DP)

4 篇文章 0 订阅

题目链接
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

示例 1:
输入:“abc”
输出:3
解释:三个回文子串: “a”, “b”, “c”

示例 2:
输入:“aaa”
输出:6
解释:6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindromic-substrings
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

此题,使用暴力的做法,显然就是枚举所有的子串,判断每个子串是不是回文串,这样的时间复杂度肯定超时。枚举就要算500,500次,加上判断是不是回文子串,时间复杂度肯定超过一秒了。
所有,我们需要研究一下回文串的规律,我们按回文串的长度来分析,怎样才能构成一个回文串。
当回文串长度为1时,显然任意字符都能构成回文串
当回文串长度为2时,显然两个字符必须相等才能构成回文串
当回文串长度为3时,显然两边的字符相等,中间字符任意,才能构成字符串
当回文串长度为4时,显然中间两个字符相等,两边字符也相等,才能构成回文串
当指定串长度为5时,显然(中间三个字符串,两边相等,中间任意),然后两边字符相等,才能构成回文串
分析到这,不难发现,
回文串长度为4的时候,中间字符串满足长度为2的回文串条件,加上两边字符相等就可以构成回文串
回文串长度为5的时候,中间字符串满足长度为3的回文串条件,加上两边字符串相等就可以构成回文串
此时,不难写出dp的动态转移方程
定义i为长度为n的字符串
定义j为当前访问到的子字符串位置
dp[i][j]为i长度的回文串到数组长度为j的位置有多少个
所有dp[i][j]的值有以下几种情况。定义字符串数组为s
当 s[j] == s[j-i]的时候
如果 i长度小于4
dp[i][j] = dp[i][j-1] + 1;
否则,需要判断中间是否为回文串,因为长度为4的时候,中间是两个字符,长度为5的时候,中间为3个字符,所有需要判断i-2的位置
即dp[i-2][j-1]-dp[i-2][j-2]!=0
不等于0证明是一个回文串
dp[i][j] = dp[i][j-1] + 1;
否则
dp[i][j] = dp[i][j-1] ;
当 s[j] != s[j-i]的时候,显然
dp[i][j] = dp[i][j-1] ;

代码如下:

public int countSubstrings(String s) {
		int len = s.length();
		int dp[][] = new int[len][len];
		dp[0][0] = 1;
		for (int i = 1; i < len; i++) {// 初始化长度为1的回文串
			dp[0][i] = dp[0][i - 1] + 1;
		}
		for (int i = 1; i < len; i++) {
			for (int j = i; j < len; j++) {
				if ((i - 1) >= 2) {// 因为长度为1,2,3的回文串不需要判断中间字符串
					if (dp[i - 2][j - 1] - dp[i - 2][j - 2] != 0) {
						if (s.charAt(j) == s.charAt(j - i)) {
							dp[i][j] = dp[i][j - 1] + 1;
						} else {
							dp[i][j] = dp[i][j - 1];
						}
					} else {
						dp[i][j] = dp[i][j - 1];
					}
				} else {
					if (s.charAt(j) == s.charAt(j - i)) {
						dp[i][j] = dp[i][j - 1] + 1;
					} else {
						dp[i][j] = dp[i][j - 1];
					}
				}
			}
		}

		/*如果还不是和理解,可以将这段代码解除注释,看一下运算过程,自己跟着算一遍,就可以深刻理解了
		 * for (int i = 0; i < len; i++) { System.out.print(String.format("%2c",
		 * s.charAt(i)) + " "); } System.out.println(); for (int i = 0; i < len; i++) {
		 * for (int j = 0; j < len; j++) { System.out.print(String.format("%2d",
		 * dp[i][j]) + " "); } System.out.println(); }
		 */

		int sum = 0;
		for (int i = 0; i < len; i++) {//加上每个长度回文串的个数,即为答案
			sum += dp[i][len - 1];
		}
		return sum;
	}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值