“回文“算法专题

1.回文数 ★

#9 判断一个整数是否为回文数

输入:121

输出:true

class Solution {
    public boolean isPalindrome(int x) {
       String numStr = String.valueOf(x);
        int len = numStr.length();
        if(len<=3){
            return numStr.charAt(0)==numStr.charAt(len-1);
        }
       for(int i=0;i<=len/2-1;i++){
           if(numStr.charAt(i)!=numStr.charAt(len-1-i)){
               return false;
           }
       }
        return true;
    }
}

2.回文排列 ★

给定一个字符串,编写一个函数判断其能否通过排列成为回文字符串

很简单,只需要判断个数为奇数个的元素是否少于1个

class Solution {
    public boolean canPermutePalindrome(String s) {
        //奇数个元素计数
        int singleCount = 0;
        char[] arr = s.toCharArray();
        HashMap<Character, Integer> map = new HashMap<>();
        for (char c : arr) {
            map.put(c, map.getOrDefault(c, 0) + 1);
        }
        for (Integer i : map.values()) {
            if (i % 2 == 1) {
                singleCount++;
            }
        }
        return singleCount <= 1;
    }
}

3.最长回文子串 ★★★

#5 给定一个字符串,找到s中最长的回文子串,设s的最大长度为1000

1.暴力查找:o(n^3)

for(int i=0;i<len;i++){
    for(int j=i;j<len;j++){
		if(j-i>maxLen && isHuiWen(arr,i,j)){
            maxLen = j-i+1;
            begin = i;
        }
  	}
}

动态规划:状态转移 o(N^2)

dp [i][j] 表示s.[i][j] 子串为回文串

  • 两层for循环:
    • 外层: j : 1~len
      • 内层:i : 0~j
      • 左右两个字符相等且(内层长1/2或者内层为回文):外层为回文dp[i][j]
      • 否则不是,继续遍历子串

理解:实际上还是先用j定位整个字符串位置,然后用i<j判断子串是否回文,在后续的判断中由于dp[][]记录下了之前的子串是否回文,因此将复杂度降低

    public String longestPalindrome(String s) {
        char[] arr = s.toCharArray();
        int len = s.length();
        if (len < 2) {
            return s;
        }
        boolean[][] dp = new boolean[len][len];
        for (int i = 0; i < len; i++) {
            dp[i][i] = true;
        }
        int maxLen = 1;
        int begin = 0;
        for (int j = 1; j < len; j++) {
            for (int i = 0; i < j; i++) {
                if (arr[i] == arr[j] && (j - i < 3 || dp[i + 1][j - 1])) {
                    dp[i][j] = true;
                }
                if(maxLen < j-i+1 && dp[i][j]){
                    maxLen = j - i + 1;
                    begin = i;
                }
            }
        }
        return s.substring(begin, begin + maxLen);
    }

4.分割回文子串 ★★★★

#131 给定一个字符串,将S分割成每个都是回文的子串

返回所有s可能的分割方案

组合问题,回溯解决

class Solution {
    List<List<String>> res = new LinkedList<>();
    public List<List<String>> partition(String s) {
        backtrack(s,0,s.length(),new LinkedList<>());
        return res;
    }
    public void backtrack(String s,int start,int len,LinkedList<String> temp){
        if(start==len){
            res.add(new LinkedList<>(temp));
            return;
        }
        for(int i=start;i<len;i++){
            if(!isHuiWen(s,start,i)){
                continue;
            }
            temp.add(s.substring(start,i+1));
            backtrack(s,i+1,len,temp);
            temp.remove(temp.size()-1);
        }
    }
    public boolean isHuiWen(String s,int start,int end){
        while(start<end){
            if(s.charAt(start)!=s.charAt(end)){
                return false;
            }
            start++;
            end--;
        }
        return true;
    }
}

每次回溯都要用“两边夹”的方式判断子串是否为回文串,于是想到用空间换时间,先初始化“子串回文矩阵”f[i][j],表示i~j的子串是否会文

class Solution {
    List<List<String>> res=new LinkedList<>(); 
    public List<List<String>> partition(String s) {
        int len = s.length();
        if (len == 0) {
            return res;
        }
        // 初始化“子串回文矩阵”:dp[i][j] 表示 s[i][j] 是否是回文
        boolean[][] dp = new boolean[len][len];
        for (int right = 0; right < len; right++) {
            for (int left = 0; left <= right; left++) {
                if (s.charAt(left) == s.charAt(right) && (right - left <= 2 || dp[left + 1][right - 1])) {
                    dp[left][right] = true;
                }
            }
        }
        backtrack(s,0,s.length(),new LinkedList<String>(),dp);
        return res;
    }
        public void backtrack(String s,int start,int len,LinkedList<String> temp,boolean[][] dp){
        if(start==len){
            res.add(new LinkedList<>(temp));
            return;
        }
        for(int i=start;i<len;i++){
            if(!dp[start][i]){
                continue;
            }
            temp.add(s.substring(start,i+1));
            backtrack(s,i+1,len,temp,dp);
            temp.remove(temp.size()-1);
        }
    }
}

5.分割回文子串Ⅱ ★★★★

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

返回符合要求的最少分割次数。

class Solution {
    int minCount = Integer.MAX_VALUE;
    public int minCut(String s) {
        backtrack(s,0,s.length(),new LinkedList<>());
        return minCount;
    }
    public void backtrack(String s,int start,int len,LinkedList<String> temp){
        if(start==0){
            minCount = Math.min(temp.size(),minCount);
            return;
        }
        for(int i=start;i<len;i++){
            if(!isHuiWen(s,start,i)){
                continue;
            }
            temp.add(s.subString(start,i+1));
            backtrack(s,i+1,len,temp);
            temp.remove(temp.size()-1);
        }
    }
    public boolean isHuiWen(String s,int start,int end){
        while(start<end){
            if(s.charAt(start)!=s.chaAt(end)){
                return false;
            }
            start++;
            end++;
        }
        return true;
    }
}

!!超时,字符串长度越长,深度越大,超时

动态规划

class Solution {
    public int minCut(String s) {
        int len = s.length();
        if (len < 2) {
            return 0;
        }
        //dp[i] 表示0-i子串分割成若干回文子串的最小分割次数
        int[] dp = new int[len];
        for (int i = 0; i < len; i++) {
            dp[i] = i;
        }
        for (int i = 0; i < len; i++) {
            if (isHuiWen(s, 0, i)) {
                dp[i] = 0;
                continue;
            }
            for (int j = 0; j < i; j++) {
                if (isHuiWen(s, j + 1, i)) {
                    dp[i] = Math.min(dp[i], dp[j] + 1);
                }
            }
        }
        return dp[len - 1];
    }
    public boolean isHuiWen(String s,int beg,int end){
        while(beg<end){
            if(s.charAt(beg)!=s.charAt(end)){
                return false;
            }
            beg++;
            end--;
        }
        return true;
    }
}

6. 验证回文字符串 ★

给定字符串中含所有可能字符,只考虑子母和数字,判断是否为回文串(可忽视大小写)

输入: "A man, a plan, a canal: Panama"
输出: true
class Solution {
    public boolean isPalindrome(String s) {
        if (s == null || s.equals(" ")) {
            return true;
        }
        int len = s.length();
        int i=0,j=len-1;
        while(i<j) {
            while (i<j && !Character.isLetterOrDigit(s.charAt(i))){
                i++;
            }
            while (i<j && !Character.isLetterOrDigit(s.charAt(j))){
                j--;
            }
            if (i < j) {
                if (Character.toLowerCase(s.charAt(i)) != Character.toLowerCase(s.charAt(j))) {
                    return false;
                }
                ++i;
                --j;
            }
        }
        return true;
    }
}
验证回文字符串Ⅱ ★★

给定一个非空字符串S,最多删除一个字符,判断是否能成为回文字符

输入: "abca"
输出: True
解释: 你可以删除c字符。
class Solution {
    public boolean validPalindrome(String s) {
		int len = s.length();
        int beg=0,end=len-1;
        while(beg<end){
            char c1 = s.charAt(beg);
            char c2 = s.charAt(end);
            if(c1==c2){
                beg++;
                end--;
            }else{
                boolean flag1 = true;
                boolean flag2 = true;
                for(int i=beg,j=end-1;i<j;i++,j--){
                    if(s.charAt(i)!=s.charAt(j)){
                        flag1 = false;
                        break;
                    }
                }
                for(int i=beg+1,j=end;i<j;i++,j--){
                    if(s.charAt(i)!=s.charAt(j)){
                        flag2 = false;
                        break;
                    }
                }
                return flag1 || flag2;
            }
        }
        return true;
    }
}

7.最长回文串 ★

给定一个包含大小写字母的字符串,找到通过这些字母构造成的最长回文串,区分大小写

输入: "abccccdd"
输出: 7
class Solution {
    public int longestPalindrome(String s) {
        int[] count = new int[128];
        for (char c: s.toCharArray())
            count[c]++;

        int ans = 0;
        for (int v: count) {
            ans += v / 2 * 2;
            if (v % 2 == 1 && ans % 2 == 0)
                ans++;
        }
        return ans;
    }
}

8. 回文子串 ★

计算一个字符串中有多少个回文子串,具有不同开始位置/结束位置的子串,即使是字符相同也认为是不同子串

class Solution {
    public int countSubstrings(String s) {
        int len = s.length();
        int res = 0;
        boolean[][] dp = new boolean[len][len];
        for (int j = 0; j < len; j++) {
            for (int i = 0; i <= j; i++) {
                if (s.charAt(i) == s.charAt(j) &&
                        (j - i <= 2 || dp[i + 1][j - 1])) {
                    dp[i][j] = true;
                    res++;
                }
            }
        }
        return res;
    }
}

9.回文链表

检查输入的链表是否会文

 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
class Solution {
    public boolean isPalindrome(ListNode head) {
        if (head == null) {
            return true;
        }
        Deque<Integer> stack = new LinkedList<>();
        Queue<Integer> queue = new LinkedList<>();
        int len = 0;
        while(head!=null){
            stack.addLast(head.val);
            queue.offer(head.val);
            head = head.next;
            len++;
        }
        len /= 2;
        while(len-->0){
            if(!stack.removeLast().equals(queue.poll())){
                return false;
            }
        }
        return true;
    }
}

快慢指针实现翻转前半链表,比较前后部分链表,时间复杂度O(n),空间O(1)

public boolean isPalindrome(ListNode head) {
	ListNode slow = head;
    ListNode fast= head;
    ListNode pre = null;
    
    while(fast!=null && fast.next!=null){
        ListNode cur = slow;
        slow = slow.next;
        fast = fast.next.next;
        cur.next = pre;
        pre = cur;
    }
    if(fast != null){
        slow = slow.next;
    }
    while(slow!=null){
        if(slow.val!=pre.val){
            return fasle;
        }
        slow = slow.next;
        pre = pre.next;
    }
    return true;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值