LeetCode之回文串系列题解

970 阅读2分钟

前言

广告: 最近github上新开了一个仓库May-Nodes,包括但不限于之前面试遇到的相关数据库,计算机操作系统,Java基础知识,计算机网络以及LeetCode等算法题解等知识。届时也会整理学习使用的PDF文档与资源。有需要的小伙伴 可以点个关注和star。在持续更新中,总会遇到你想要的。

回文串: 回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串(解释来源于百度百科)。对于系列的回文串相关的算法涉及到有动态规划,中心扩散法,以及递归回溯等等,可以用来考量的知识点也是比较多的,以下文章将从LeetCode的每日一题入手,来具体总结相关的几个题目:

计数二进制子串

每日一题:696- 计数二进制子串

使用位扩展的解法:

      private int count = 0;
    public int countBinarySubstrings(String s) {
        for(int i = 1; i < s.length(); i++) {
         if(s.charAt(i-1)!= s.charAt(i))
         BinarySubstring(s,i-1,i);
        }
        return count;
    }
    public void BinarySubstring(String s, int start, int end) {
        char f = s.charAt(start);
        char e = s.charAt(end);
        while(start >= 0 && end < s.length() && s.charAt(start) == f && s.charAt(end) == e) {
            start--;
            end++;
            count++;
        }
    }

回文子串

回文子串

暴力求解

    public int countSubstrings(String s) {
  int count = 0;
        for(int i = 0;i<s.length();i++){
            for(int j= i;j<s.length();j++){
                if(getCount(s.substring(i,j+1)))
                    count++;
            }
        }
        return count;

    }
    private  static  boolean getCount(String s){
        int i= 0;
        int j= s.length()-1;
        while(i<=j){
            if(s.charAt(i)!=s.charAt(j))
            {
                return  false;
            }
            i++;
            j--;
        }
        return   true;
    }

使用动态规划

首先明白,这个dp[i][j] 的含义,表示对于 字符串进行从i到j长度的字符串是不是回文串:

  1. 如果两个字符相等的时候,转移方程 dp[i][j] =dp[i+1][j-1];
  2. 进行特判: 表示两个是相邻且相等时候 。
  3. 进行值的求加。
 public int countSubstrings(String s) {
      if(s == null || s.equals("")){
            return 0;
        }
      int len = s.length();
        int result = len;
        boolean [][] dp = new boolean[len+1][len+1];
        for(int i = 0; i < len; i++){
            dp[i][i] = true;
  // 首先明白这个动态规划的dp数组是什么意思
        }
     /**
     根据不同的情况进行初值的设置 明白为什么不直接使用到 
     for(int  i= 0;i<len-1;i++) 会出现值的未定义变复制的情况。
     **/
     
        for(int i = len - 1;i>= 0 ;i--){
            for(int j = i+ 1;j<len; j++){
                if(s.charAt(i) ==s.charAt(j)){
                    if(j- i == 1)
                        dp[i][j] = true;
                    else
                        dp[i][j] =dp[i+1][j-1];
                    if(dp[i][j])
                        result++;
                }
            }
        }

        return  result;
        
    }

使用中心扩散方法

 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;
    }

最长回文子串

最长回文子串

动态规划

大体思想一致还是一致,进行必要的标记标记值。

  public String longestPalindrome(String s) {
   int start = 0;
        int maxlen = 1;
        int len =s.length();
        if(len<2)
            return  s;
        boolean [][] dp = new boolean[len+1][len+1];
        for(int i= 0 ;i<s.length();i++){
            dp[i][i] = true;
        }
        for(int i =len-1;i>=0 ;i--){
            for(int j= 1+i;j<len;j++ ){
                if(s.charAt(i)== s.charAt(j)){
                    if(j-i<3){
                        dp[i][j] = true;
                    }
                    else
                        dp[i][j] =dp[i+1][j-1];
                }
                else
                    dp[i][j] = false;
                if(dp[i][j] && j-i+1>maxlen){
                    maxlen= j-i+1;
                    start = i;
                }
            }
        }
        return  s.substring(start,maxlen+start);
    }   

中心扩散

    public String longestPalindrome(String s) {
        // ababa 求最长公共子串
        int len = s.length();
        String result = "";

        for (int i = 0; i < len * 2 - 1; i++) {
            int left = i / 2;
            int right = left + i % 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;
    }

回文串的切割

切割回文串

利用到回溯与递归

 public List<List<String>> partition(String s) {
        List<List<String>> lists = new ArrayList<>();
        List<String> list = new ArrayList<>();
        palindrome(s,list,lists);
        return  lists;
    }
    private static  void palindrome(String s,List<String> list,List<List<String>> lists)
    {
        if(s.length()==0)
        {
            lists.add(new ArrayList<>(list));
            return;
        }
         int len = s.length();
        for(int i = 0;i<=len;i++){
                String p = s.substring(0,i);
                if(ishuiwen(p)){
                    list.add(p);
                    palindrome(s.substring(i,len),list,lists);
                    list.remove(list.size()-1);
                }
        }
    }
    /**
     * 判断是否回文的小函数
     * @param args
     */
    private  static  boolean ishuiwen(String s){
        if(s.length()==0)
        return false;
        int left  =0;
        int right = s.length()-1;
        while(left< right){
            if(s.charAt(left)!= s.charAt(right))
                return  false;
            left++;
            right --;
        }
        return  true;
    }

回文串切割-II

切割回文串-ii

利用到动态规划的思想(重要)

   public int minCut(String s) {
        int len= s.length();
        int [] dp = new int[len+1];
        for(int i =0;i<len;i++){
            dp[i]=i;
        }
        for(int i= 1;i<s.length();i++){
            if(ishuiwen(s,0,i))
            {
                dp[i] =0;
            }
            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];
    }
    private  static  boolean ishuiwen(String s,int left,int right){
        while(left< right){
            if(s.charAt(left)!= s.charAt(right))
                return  false;
            left++;
            right --;
        }
        return  true;
    }