1. 回文子串

(1)中心扩展

  • 比如对一个字符串 ababa,选择最中间的 a 作为中心点,往两边扩散,第一次扩散发现 left 指向的是 b,right 指向的也是 b,所以是回文串,继续扩散,同理 ababa 也是回文串。
  • 这个是确定了一个中心点后的寻找的路径,然后我们只要寻找到所有的中心点,问题就解决了。
  • 中心点一共有多少个呢?看起来像是和字符串长度相等,但你会发现,如果是这样,上面的例子永远也搜不到 abab
  • 中心点不能只有单个字符构成,还要包括两个字符,比如上面这个子串 abab,就可以有中心点 ba 扩展一次得到,所以最终的中心点由 2 * len - 1 个,分别是 len 个单字符和 len - 1 个双字符。
class Solution{public int countSubstrings(String s) {int ans = 0;for (int center = 0; center < 2 * s.length() - 1; center++) {// left和right指针和中心点的关系是?// 首先是left,有一个很明显的2倍关系的存在,其次是right,可能和left指向同一个(偶数时),也可能往后移动一个(奇数)// 大致的关系出来了,可以选择带两个特殊例子进去看看是否满足。int left = center / 2;int right = left + center % 2;while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {ans++;left--;right++;}}return ans;}
}

这个解法也同样适用于下面一个题:最长回文子串

(2)动规

  • 状态:dp[i][j] 表示字符串s在[i,j]区间的子串是否是一个回文串。
  • 状态转移方程:当 s[i] == s[j] && (j - i < 2 || dp[i + 1][j - 1]) 时,dp[i][j]=true,否则为false

解释一下:

  • 当只有一个字符时,比如 a 自然是一个回文串。
  • 当有两个字符时,如果是相等的,比如 aa,也是一个回文串。
  • 当有三个及以上字符时,比如 ababa 这个字符记作串 1,把两边的 a 去掉,也就是 bab 记作串 2,可以看出只要串2是一个回文串,那么左右各多了一个 a 的串 1 必定也是回文串。所以当 s[i]==s[j] 时,自然要看 dp[i+1][j-1] 是不是一个回文串。
class Solution {public int countSubstrings(String s) {// 动态规划法boolean[][] dp = new boolean[s.length()][s.length()];int ans = 0;for (int j = 0; j < s.length(); j++) {for (int i = 0; i <= j; i++) {//dp[i + 1][j - 1] : 向里面缩if (s.charAt(i) == s.charAt(j) && (j - i < 2 || dp[i + 1][j - 1])) {dp[i][j] = true;ans++;}}}return ans;}
}

2. 最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。

示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。

示例 2:
输入:s = “cbbd”
输出:“bb”

示例 3:
输入:s = “a”
输出:“a”

示例 4:
输入:s = “ac”
输出:“a”

提示:

1 <= s.length <= 1000
s 仅由数字和英文字母(大写和/或小写)组成

(1)暴力法

class Solution {public String longestPalindrome(String s) {String ans = "";int max = 0;int len = s.length();for (int i = 0; i < len; i++)for (int j = i + 1; j <= len; j++) {String test = s.substring(i, j);if (isPalindromic(test) && test.length() > max) {ans = s.substring(i, j);max = Math.max(max, ans.length());}}return ans;}public boolean isPalindromic(String s) {int len = s.length();for (int i = 0; i < len / 2; i++) {if (s.charAt(i) != s.charAt(len - i - 1)) {return false;}}return true;}
}

(2)动态规划

dp[i][i]=1; //单个字符是回文串
dp[i][i+1]=1 if s[i]=s[i+1]; //连续两个相同字符是回文串

class Solution {// 动态规划法public String longestPalindrome(String s) {int len = s.length();if (len < 2) {return s;}int maxLen = 1;int begin = 0;char[] cs = s.toCharArray();// dp[i][j]:表示s[i][j]是否是回文串boolean[][] dp = new boolean[len][len];// 初始化:单独一个字符肯定是回文子串for (int i = 0; i < len; i++) {dp[i][i] = true;}// 经验:dp区域是正方形的话,通常左下角区域无效不需要再填,因为走过的区域不用再走for (int j = 1; j < len; j++) { // 上三角区域,按列从上到下填for (int i = 0; i < j; i++) {// 首尾不相等时,必不是回文串if (cs[i] != cs[j]) {dp[i][j] = false;} else {// 首尾相等时,有2种情况// 情况1:s[i...j]长度不超过3,不用检查必为回文串// 情况2:s[i...j]长度大于3,由s[i+1...j-1]来判断dp[i][j] = j - i + 1 <= 3 || dp[i + 1][j - 1];}// 更新max和beginif (dp[i][j] && j - i + 1 > maxLen) {maxLen = j - i + 1;begin = i;}}}return s.substring(begin, begin + maxLen);}
}

这样写或许更容易理解(更清晰):

class Solution {public String longestPalindrome(String s) {int len = s.length();if (len < 2)return s;int maxStart = 0;  //最长回文串的起点int maxEnd = 0;    //最长回文串的终点int maxLen = 1;  //最长回文串的长度char[] cs = s.toCharArray();// dp[i][j]:表示s[i][j]是否是回文串boolean[][] dp = new boolean[len][len];for(int right = 1; right < len; right++) {for(int left = 0; left < right; left++) {               if (cs[left] == cs[right] && (right - left <= 2 || dp[left + 1][right - 1])) {dp[left][right] = true;// 更新max和beginif (right - left + 1 > maxLen) {maxLen = right - left + 1;maxStart = left;maxEnd = right;}}} }return s.substring(maxStart, maxEnd + 1);}
}

(3)中心扩展法

回文中心的两侧互为镜像。因此,回文可以从他的中心展开,并且只有 2n-1 个这样的中心(一个元素为中心的情况有 n 个,两个元素为中心的情况有 n-1 个)

class Solution {public String longestPalindrome(String s) {if (s == null || s.length() < 1){return "";}// 初始化最大回文子串的起点,最大回文子串长度int start = 0;int maxLen = 1;// 遍历每个位置,当做中心位for (int i = 0; i < s.length(); i++) {// 分别拿到奇数偶数的回文子串长度int len_odd = expandCenter(s,i,i);int len_even = expandCenter(s,i,i + 1);// 对比最大的长度int len = Math.max(len_odd,len_even);// i为当前回文的中心:奇数回文为中心下标,偶数回文为一条虚线(i,i + 1)// 计算对应最大回文子串的起点和终点,最长子串长度//如果长度比原本记录的长度大了,说明有更长的回文出现了,所以要记录一下回文的下标if (len > maxLen){// 根据i和maxLen算start下标// 奇数:i-maxLen/2// 偶数:i-maxLen/2+1// 统一:i-(maxLen-1)/2//这里为什么要i-1?,这里说明一下,因为for循环是从0开始的,//如果是奇数回文,假设有个回文是3个,那么len=3,此时中心i是下标1(从0开始),那么(len-1)/2和len/2的结果都是1,因为整型会向下取整//但是如果是偶数回文,假设有个回文是4个,那么len=4,此时的中心是一条虚线,但是i的位置却在1,(因为S是从左向右遍历的,如果从右向左,//i的位置就会在2.)这时候,(len-1)/2=1,len/2=2.很明显为了保证下标正确,我们需要的是(len-1)/2.原因其实是i在中心线的左边一位,//所以要少减个1.maxLen = len;start = i - (len - 1)/2;}}return s.substring(start, start + maxLen);        //s.substring(3,5) 切割 [3,5),左闭右开}private int expandCenter(String s,int left,int right){// left = right 的时候,此时回文中心是一个字符,回文串的长度是奇数// right = left + 1 的时候,此时回文中心是一个空隙,回文串的长度是偶数// 跳出循环的时候恰好满足 s.charAt(left) != s.charAt(right)while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)){left--;right++;}//这里其实是right-left+1-2,意思就是right-left+1是本来的长度,但是由于上面最后一次判断肯定false,所以最后一次left--和right++//其实不属于回文的一部分,所以要减去2return right - left - 1;}
}

简化一下代码:

class Solution {public String longestPalindrome(String s) {if (s == null || s.length() < 1){return "";}String result = "";int len = s.length();for (int center = 0; center < len * 2 - 1; center++) {int left = center / 2;int right = left + center % 2;while (left >= 0 && right < len && s.charAt(left) == s.charAt(right)) {String tmp = s.substring(left, right + 1);if (tmp.length() > result.length())result = tmp;left--;right++;}}return result;}
}

3. 最长回文子序列

(1)动规

状态:

  • dp[i][j] 表示 s 的第 i 个字符到第 j 个字符组成的子串中,最长的回文序列长度是多少。

转移方程:

  • 如果 s 的第 i 个字符和第 j 个字符相同的话: dp[i][j] = dp[i + 1][j - 1] + 2
  • 如果 s 的第 i 个字符和第 j 个字符不同的话: dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
  • 然后注意遍历顺序,i 从最后一个字符开始往前遍历,j 从 i + 1 开始往后遍历,这样可以保证每个子问题都已经算好了。
    • 从递推公式dp[i][j] = dp[i + 1][j - 1] + 2 和 dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]) 可以看出,dp[i][j]是依赖于dp[i + 1][j - 1] 和 dp[i + 1][j],
    • 如果我们想求dp[i][j],那么其他3个必须都是已知的,很明显从上往下遍历是不行的,我们只能让i从最后一个字符往前遍历,j从i的下一个开始遍历,最后只需要返回dp[0][length - 1]即可。

初始化:

  • dp[i][i] = 1 单个字符的最长回文序列是 1
class Solution {public int longestPalindromeSubseq(String s) {int len = s.length();int[][] dp = new int[len][len];//这里i要从最后一个开始遍历for(int i = len-1; i >= 0; --i) {for(int j = i; j < len; ++j) {//单个字符也是一个回文串if(i == j) {dp[i][j] = 1;continue;} //j从i的下一个开始if(s.charAt(i) == s.charAt(j)) {dp[i][j] = dp[i+1][j-1] + 2;} else {dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);}} }return dp[0][len-1];}
}

Leetcode——回文子串 / 最长回文子串 / 最长回文子序列相关推荐

  1. 怎么判断一个字符串的最长回文子串是否在头尾_LeetCode 5 迅速判断回文串的Manacher算法...

    本文始发于个人公众号: TechFlow 题意 Given a string s, find the longest palindromic substring in s. You may assum ...

  2. 最长公共子序列|最长公共子串|最长重复子串|最长不重复子串|最长回文子串|最长递增子序列|最大子数组和...

    最长公共子序列|最长公共子串|最长重复子串|最长不重复子串|最长回文子串|最长递增子序列|最大子数组和 文章作者:Yx.Ac   文章来源:勇幸|Thinking (http://www.ahathi ...

  3. 数组字符串那些经典算法:最大子序列和,最长递增子序列,最长公共子串,最长公共子序列,字符串编辑距离,最长不重复子串,最长回文子串 (转)...

    作者:寒小阳 时间:2013年9月. 出处:http://blog.csdn.net/han_xiaoyang/article/details/11969497. 声明:版权所有,转载请注明出处,谢谢 ...

  4. 信息学奥赛一本通 1309:【例1.6】回文数(Noip1999) | 洛谷 P1015 [NOIP1999 普及组] 回文数

    [题目链接] ybt 1309:[例1.6]回文数(Noip1999) 洛谷 P1015 [NOIP1999 普及组] 回文数 注:两OJ上的问题考察内容相同,但输出要求不同 [题目考点] 1.高精度 ...

  5. 回文数判断python五位数_Python【习题】回文数:判断一个数是否是回文数

    人生苦短,我用Python 环境:Windows 10 64-bit, python == 3.6.4 , PyCharm CE == 2018.1 什么是回文数: 有这样一类数,他们顺着看和倒着看是 ...

  6. Python-打印指定范围内的全部回文素数(高教社,《Python编程基础及应用》习题8-7) (10分) 回文素数是指一个数既是素数又是回文数,例如131既是素数又是回文数。

    前言 PTA程序设计类教学平台-Python作业 题目: 回文素数是指一个数既是素数又是回文数,例如131既是素数又是回文数.请实现下述两个函数,帮助测试程序完成如下功能:从键盘输入正整数N, 打印从 ...

  7. 递归判断回文字符串c语言,递归判断字符串是否为回文

    源代码 package test; import java.util.Scanner; public class Palindrome { public static void main(String ...

  8. 输入一个字符串字,如果是“回文”输出“Yes”,否则输出“No”。所谓“回文”,是指顺读和倒读都一样的字符串。

    输入一个字符串字,如果是"回文"输出"Yes",否则输出"No".所谓"回文",是指顺读和倒读都一样的字符串. 如: & ...

  9. LeetCode 每日一题 3. 无重复字符的最长子串

    LeetCode 每日一题 3. 无重复字符的最长子串   大家好,我叫亓官劼(qí guān jié ),在CSDN中记录学习的点滴历程,时光荏苒,未来可期,加油~博客地址为:亓官劼的博客 本文原创 ...

  10. 大厂高频面试系列02--最长不重复子串问题(LeetCode)

    大厂高频面试系列02--最长不重复子串问题(LeetCode) 题目描述: 给定一个字符串,找出不含有重复字符的最长子串的长度. 举例: 给定 "abcabcbb" ,没有重复字符 ...

最新文章

  1. 使用 jetty-maven-plugin发布maven项目
  2. [JZOJ P1311] [DP]邮局设置问题
  3. intvar mysql_mysql binlog格式解析(一)
  4. 在IBM服务器安装Windows server 2012的心得
  5. mmap函数_Linux内存映射mmap原理分析
  6. 【笔记】MySQL的基础学习(二)
  7. C/C++编程(尾积相乘)
  8. Thinkphp精仿韩都衣舍商城网站源码
  9. 阿里菜鸟java后台开发电话面试
  10. 开源项目:羚羊-抖音运营平台,基于Vue+抖音开放平台+小白开放平台+PhalApi开源框架的抖音运营管理平台
  11. openwrt 程序C++ hello world的ipk软件包(三)
  12. 首次使用计算机 鼠标没反应,电脑鼠标没反应是怎么回事
  13. Python实现二维码、条形码识别
  14. Java指导书练习题——抽象类
  15. PC 微信 Hook 实战记录 1: 找到个人信息
  16. learning的反义词英文_英语同义词反义词
  17. 〖产品思维训练白宝书 - 产品思维认知篇⑦〗- 聊一聊 产品经理 的工作内容与职责划分
  18. linux运行fuzzbunch,方程式0day ETERNALBLUE复现之Empire ampamp; Msfconsole下的she...
  19. 给出一个123 456的矩阵,输出转置矩阵
  20. 邮箱手机号正则表达式

热门文章

  1. vue + webpack 实用技巧
  2. mysql凡人入门_SQL入门学习
  3. AT命令发短信和GPRS通信
  4. C语言实现通讯录【二】(动态内存开辟,文件数据存储)
  5. Redis的序列化配置
  6. 中文最常用600句短语地道英文表达
  7. Wondershare PDFelement 8 Pro Mac(PDF编辑工具)
  8. 不为人知的地下暗流:揭秘色情推广“影科技”家族背后的流量生意
  9. 台式电脑拍照软件_电脑维护的几点注意
  10. GoldenGate 日常监控