新春归来,写的第一篇博客,博客虽停 但是算法题还是有在刷,今个就总结一下回文数系列的题目。
回文数 即正序倒序都是一样的,如121,1221,aba,abcdedcba都是回文数
回文数字
上题来源牛客网56,今个力扣崩了进不去哈哈哈
我们很容易想到反转提供的数字或文字,但是数字容易溢出(即求余数再*10转化会超出Integer.MAX_VALUE),一个可行的方法是只反转一半。
public boolean isPalindrome (int x) {
if(x==0) return true;
if(x<0 || x%10==0) return false;//10 20 ...100和负数肯定不是回文数 直接排除
int num=0;
int tem=x;
int rem=0;
while(tem>num){
rem=tem%10;
num=num*10+rem;
tem=tem/10;
}
return (num/10)==tem || num==tem;
}
回文字符串
数字取余再反转,字符串呢?
我们也可以通过反转的方式求
a构建一个新的字符串 然后通过自带的api reverse()及equals() 比较
if(str.length()<1) return false;
StringBuffer tem=new StringBuffer();
int n=str.length();
for(int i=0;i<n;i++){
tem.append(str.charAt(i));
}
//先将字符反转 再toString()
return tem.reverse().toString().equals(str.toString());
注:
StringBuffer() 提供了字符串反转功能reverse()
StringBuffer()和String类型的字符进行比较时,通过tostring()后用equals()比较
b直接给StringBuffer()类赋值
StringBuffer()和String类并不等同,二者比较要先转化为string,
赋值的话可以挨个char字符赋值,直接赋值 可以如以下
StringBuffer out=new StringBuffer();
out.append(str);//String 和StringBuffer() 无法直接转化。
return out.reverse().toString().equals(str.toString());
c不同过api,用双指针 挨个比较
int n=str.length();
int left=0;
int right=n-1;
while(left<right){
if(str.charAt(left)!=str.charAt(right)){
return false;
}
left++;
right--;
}
当然字符串可以设置一些其他要求:如只认定数字和字符
那么在给新的String赋值的时候 ,可以筛除一些。
# 判断但链表是否符合回文子串
单链表只能往下走不能回头
a设置快慢指针 对比
因为回文链表是关于中间结点(若存在)对称的,我们可以设置两个快慢指针(slow 和 fast)以获得中间结点。然后反转后部分链表 再和head挨个对比元素
ListNode slow=head,fast=head;
//01寻找到中间结点
//偶数:slow 以后 包括 slow结点
//奇数:slow.next之后都是
while(fast!=null && fast.next!=null){
fast=fast.next.next;
slow=slow.next;
}
//分奇数偶数个结点
if(fast!=null){
//快指针 不为null 此链表为奇数个
slow=slow.next;
}
//反转后半部分
slow=reverseNode(slow);
//比较head和slow链表
while(slow!=null){
if(slow.val!=head.val){
return false;
}
slow=slow.next;
head=head.next;
}
return true;
反转链表:
private ListNode reverseNode(ListNode head){
if(head==null || head.next==null) return head;
ListNode res=reverseNode(head.next);
head.next.next=head;
head.next=null;
return res;
}
求链表是否为回文结构 还可以使用栈依次比较
b用递归求
先进入递归到最后一个
然后函数之外的head从头开始 这样就可以挨个比较了
public class Solution {
ListNode temp;
public boolean isPail (ListNode head) {
// write code here
temp=head;
return check(head);
}
private boolean check(ListNode head){
if(head==null) return true;
boolean res=check(head.next) && (temp.val==head.val);
temp=temp.next;
return res;
}
}
求字符串中打乱之后最长的回文子串
利用HashSet 求解最后保存个数
class Solution {
public boolean canPermutePalindrome(String s) {
//字符打乱 重新排列
//那么一个字符串
//偶数: 所有字符数目都是双数
//奇数: 一个奇数目+ 其他双数目
//哈希表是键值对
//HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。
if(s==null) return true;
char[] c=s.toCharArray();
Set<Character> m=new HashSet<>();
for(char tem:c){
if(m.contains(tem)){
m.remove(tem);
}else{
m.add(tem);
}
}
return m.size()<=1;
}
}
最长回文子串
a中心扩散法
public class Solution {
public int getLongestPalindrome (String A) {
// write code here
//中心扩散法
int n=A.length();
if(n<=1) return n;
int longPal=0;//最长回文子串
int left,right;
for(int i=0 ; i<n ; ){
//求解最长 longPal保存最长的
if( (n-i) < longPal /2) break;
left=right=i;
//去除重复的 n-1 避免越界
while(right < n-1 && A.charAt(right+1)==A.charAt(right)){
++right;
}
//修改i的位置
i=right+1;
while(left>0 && right<n-1 && A.charAt(right+1)==A.charAt(left-1)){
left--;right++;
}
//保留最长的
if( (right-left+1)>longPal ){
longPal=( right-left+1 );
}
}
return longPal;
}
}
b动态规划解法
确定状态方程
初始边界
状态转移
01设置一个布尔类型的dp数组
02初始化 对角线 两组元素相同
03
如果两个字符相等 且相邻 那么这两个元素回文 dp[i][j]=true
如果两个字符串相等 不相邻,但内部字符串回文 即回文
(A.charAt(i)==A.charAt(j) && ((j+1==i) || dp[i-1][j+1]) )
两个细节之处 ab 回文最长为1
i是最末尾字符 j是开始字符 那么判断二者内部元素是否为回文 可用dp[i-1][j+1]
public class Solution {
public int getLongestPalindrome (String A) {
// write code here
int n=A.length();
if(n<=1) return n;
boolean[][] dp=new boolean[n][n];
int maxLen=1;
for(int i=0;i<n;i++){//i行 j列
dp[i][i]=true;
for(int j=0;j< i ;j++){
if(A.charAt(i)==A.charAt(j) && ((j+1==i) || dp[i-1][j+1]) ){//两种可能 1相邻相等 2不相邻相等 那么内部要回文(A.charAt(i-1)==A.charAt(j+1))
dp[i][j]=true;//判断内部 回文 dp[i-1][j+1] j是后面的字幕 i是前面的字母 即最前面的
}
if(dp[i][j] && maxLen<(i-j+1)){
maxLen=i-j+1;
}
}
}
return maxLen;
}
}