这一篇,目的只有一个,将回文数这个概念解析明白(之前自己也是半懂状态),希望这一篇可以加深对此的理解和问题的解决。
一.判断回文数
1.概念
首先什么是回文数呢?
121,aba,ababa…这种形式就是了,反转过来还是一样的,这里我们统一看回文字符串就行了。
2.双指针方法
-
双指针,一头一尾,逐一比较是否相等,相等的话,指针进行移动,直到两个指针相遇,不等的话直接返回flase。
-
示例代码如下:
//判断回文串的双指针方法
public static boolean isPalindrome(String str) {
int left = 0;
int right = str.length() - 1;
while (left < right) {
if (str.charAt(left) != str.charAt(right)) {
return false;
}
left++;
right--;
}
return true;
}
3.利用栈
- 首先,栈是一种特殊的数据结构,先进去的沉入底部,先进去的后出来,类似于一口井。
我们只需要判断进去的字符串和出来的字符串相等即可。因为进去和出来相当于做了一次字符串的翻转。 - 栈的常用方法:
push()进栈
pop()出栈,并返回出来的字符
peek()取栈最顶部的值,但不出栈
isEmpty():判断栈是否为空,返回true和flase
示例代码如下:
// 进栈字符=出栈字符
public static boolean isPalindrome2(String str) {
Stack<Character> stack = new Stack<>();
for (int i = 0; i < str.length(); i++) {
stack.push(str.charAt(i));
}
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) != stack.pop()) {
return false;
}
}
return stack.isEmpty();
}
二.第5题.最长回文子串
知道了上面的回文字符串的基本判断方法,我们开始走进这到力扣的中等题。
题目要求如下:
给你一个字符串 s,找到 s 中最长的回文子串。
如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。
示例:
2.1暴力算法
- 首先,这个写一个方法isPalindrome(char [] chars,int left,int right),用来判断字符数组left到right是否为回文字符串。
- 说是暴力,自然是一一比较,双for循环,一个是起点一个是终点,一个个找回文子串,然后更新一个起点能够找到的最长回文子串。
- 起点从第一个字符开始移动,移动到倒数第二个字符,防止越界。
- 其中边界条件是:只有一个字符或者是空字符,那就是它本身。
- 示例代码和注释如下:
//1. 暴力算法,时间O(n^3)
public String longestPalindrome1(String s) {
//边界条件
int len=s.length();
if(len<2) return s;
//初始化,转化为字符串数组(防止越界)
int MaxLen=1; //最长回文子串的长度
char[]chars=s.toCharArray();
int begin=0; //起始位置
//双循环+回文数判断爆破解决
//起点i,遍历的字符串终点j
for(int i=0;i<len-1;i++){
for (int j=i+1;j<len;j++){
//如果i,j之间的字符串为回文字符串的话
// 并且最长长度更新的话进入判断,进行数据更新
if(isPalindrome(chars,i,j)&& j-i+1>MaxLen){
MaxLen=j-i+1;
begin=i;
}
}
}
//截取最长子串,起始,终止(起点+长度)
return s.substring(begin,begin+MaxLen);
}
//判断字符串数组中,左右两边中的字符串是否为回文字符串
public static boolean isPalindrome(char [] chars,int left,int right) {
while (left < right) {
if (chars[left] != chars[right]) {
return false;
}
left++;
right--;
}
return true;
}
2.2中心发散法
-
因为一个字符串是回文字符串,那么它的子串(取两边)也是回文字符串,是一种天然的递归思想。
-
所以这次的目标就是找到中心点,向外扩散,找到最长的字符串,然后刚刚中心点的最长字符串再比较。
-
其中 expandAroundCenter(String s,int left,int right)用来寻找中心点的最长字符串长度,其中将奇数和偶数的状况统一了,left=right为奇数状态,right=left+1为偶数状态。
-
时间复杂度:O(n^2),空间复杂度:O(1),其中力扣官方的示例图如下:
-
示例代码如下:
//2.中心扩散法,时间O(2n)
public String longestPalindrome2(String s) {
//边界条件
if(s.length()<1 ||s==null) return "";
//初始化起始点和终止点
int start=0,end=0;
//主要逻辑代码
for(int i=0;i<s.length();i++){
//回文长度为偶数和奇数
int oddLen=expandAroundCenter(s,i,i);
int envLen=expandAroundCenter(s,i,i+1);
//两种情况都概括了,求较大值
int length=Math.max(oddLen,envLen);
//更新起始点和终止点
if(length>end-start){
//其中(length-1)/2目的是向下取整,奇偶数统一
start=i-(length-1)/2;
end=i+length/2;
}
}
//截取最长子串,起始,终止(起点+长度)
return s.substring(start,end+1);
}
//中心扩散方法:先找到中心,左右扩散对比,找到最长的回文字符串
public int expandAroundCenter(String s,int left,int right){
//在边界条件内,满足回文条件,继续向外扩散找到最长的位置
while (left>=0&&right<s.length()&&s.charAt(left)==s.charAt(right)){
--left;
++right;
}
//返回长度,left和right为指针,不包括他们所指向的字符串
//长度=r-l+1-2
return right-left-1;
}
2.3 动态规划
- 回文字符串,和上面的思想类型,它要是一个回文字符串,那它去掉两边后的子串也要是回文字符串。
- 状态转移方程:dp[i][j]=dp[i+1][j-1]&&s[i]==s[j](字符串相等)
- 边界条件:j-i+1<4;字符串长度为2和3,不需要检验子串是否是回文,肯定是,因为单个字符和空不需要验证。
- 对角线表示当个字符,所以dp[]i[i]=true
- 这一部分没有具体的图,不方便理解,所以这是官方讲解中的ppt示例:
- 代码示例:
//方法3:动态规划
public String longestPalindrome(String s) {
int n = s.length(); // 字符串长度
boolean[][] dp = new boolean[n][n]; // 动态规划数组,记录子问题的结果
String res = ""; // 存储结果的字符串
int begin = 0; // 最长回文子串的起始位置
int maxLen = 0; // 最长回文子串的长度
for (int L = 1; L <= n; L++) { // 遍历子字符串的长度
for (int i = 0; i < n; i++) { // 遍历起始索引
int j = i + L - 1; // 结束索引
if (j >= n) { // 如果结束索引超出字符串长度,跳出当前循环
break;
}
if (s.charAt(i) != s.charAt(j)) { // 如果首尾字符不相等,不是回文
dp[i][j] = false;
} else {
// 判断内部子串是否为回文,其中长度为1看定为回文
dp[i][j] = L <= 2 || dp[i + 1][j - 1];
}
// 如果当前子串是回文且长度大于最长回文子串长度
if (dp[i][j] && L > maxLen) {
begin = i; // 更新起始位置
maxLen = L; // 更新最长长度
}
}
}
// 根据起始位置和最长长度提取回文子串并返回
return s.substring(begin, begin + maxLen);
}