zoukankan      html  css  js  c++  java
  • Longest Palindromic Substring笔记

    这是一道在leetcode上看到的题目

    一开始,我能想到的思路是蛮力法:

    就遍历每个字符,然后对每个字符都尝试从1到n的长度,看有没有回文串,并记录以该字符起始的回文串的最大长度。
    这个思路其实没有上手实现,因为和前面的那题求最大没有重复字符的substring的原始思路一样,时间复杂度是O(n^3),是超时的。

    然后是可以通过的方法:

    两边扩展法:

    抓住,回文串是轴对称的这个特点,即一个中心往两边探索,两边都是相等的。
    实现上先要遍历每个字符,从0序号到s.length()-2序号,这里的遍历,是把它们作为中心。
    然后每个中心,进行两个判断:
      1.以这个字符为中心可能有字符个数为奇数的回文串,即以这个字符为中心,往两边扫描,两边的都相等。 扫描到不相等的地方,记下长度,就是以该字符为中心的最大的奇数回文串的长度。
      2.以这个字符为中心可能有字符个数为偶数的回文串,即这个时候的对称中心为两个字符——像例如abccba,当遍历到c元素的时候,如果只是以它为中心,往两边扩散,会立刻发现b!=c,然后就判断不出是回文串了。


    因此遍历的时候要对每个字符都进行两次判断,时间复杂度为O(n^2)

    class Solution {
        
        /**
        *用的思路是 遍历每个元素,然后对每个元素进行两边扫描,扫描的同时可以判断是否回文串了,因为回文串必定是轴对称。
        
        *对每个元素两边扫描的时候要分两种情况:
            1.因为以一个元素为中心,可能构成奇数个字符的  回文串,也就是只关于那个元素对称;
            2.又或者是构成偶数个字符的  回文串,这个时候就不止关于那个元素对称,还有它旁边的元素,例如abccba,当遍历到c元素的时候,如果只是以它为中心,往两边扩散,会立刻发现b!=c,然后就判断不出是回文串了。
        因此遍历的时候要对每个字符都进行两次判断,时间复杂度为O(n^2)
        */
        public String longestPalindrome(String s) {
            
            if(s.length() == 1)return s; //因为  如果s长度为1,进不了下面那个for循环……
            
            int longestPalindromeLength = 0;
            
            String longestPalindromeStr = "";
            
            for(int i = 0; i < s.length() - 1; i++) {
                //遍历每个字符,以每个字符为中心  进行判断以这个  字符为中心的所有字符串中有没  回文字符串,并求出最大的那个
                int oddLongestLength = findLongestPalindrome(s, i);
                if(oddLongestLength > longestPalindromeLength) {
                    longestPalindromeLength = oddLongestLength;
                    longestPalindromeStr = s.substring(i - oddLongestLength/2, i + oddLongestLength/2 + 1);//注意这个subString后面那个index是不取的……
                }
                
                int evenLongestLength = findLongestPalindrome(s, i, i + 1);
                if(evenLongestLength > longestPalindromeLength) {
                    longestPalindromeLength = evenLongestLength;
                    longestPalindromeStr = s.substring(i + 1 - evenLongestLength/2, i + evenLongestLength/2 + 1);//注意这个subString后面那个index是不取的……
                }
            }
            
            return longestPalindromeStr;
        }
        
        
        /**
         * 以一个index为中心,找以它为中心的  所有  回文字符串  而且是  字符个数为奇数的回文字符串,因为偶数的话,中心应该是两个。
         * @param s 字符串
         * @param centerIndex 中心字符的index
         * @return 那个最长的  回文字符串的  长度
         */
        private int findLongestPalindrome(String s, int centerIndex) {
            int tempLongestLength = 0;//代表以这个centerIndex为中心的所有回文串中的  最大
            
            for(int i = centerIndex, j = centerIndex; i >= 0 && j <= s.length() - 1; i--, j++) {
                if(s.charAt(i) == s.charAt(j)) {
                    //说明出现了  一个回文串了
                    if( (j - i + 1) > tempLongestLength ) {
                        tempLongestLength = j - i + 1;
                    }
                } else {
                    //如果不相等,说明不对称  直接break了
                    break;
                }
            }
            return tempLongestLength;
        }
        
        
        /**
         * 找以 这两个index为中心,个数为偶数的  所有  的回文字符串
         * @param s 字符串
         * @param leftCenterIndex 左边中心
         * @param rightCenterIndex 右边中心
         * @return 这两个中心拓展的最长 的  回文字符串的长度
         */
        private int findLongestPalindrome(String s, int leftCenterIndex, int rightCenterIndex) {
            int tempLongestLength = 0;//代表以这个centerIndex为中心的所有回文串中的  最大
            
            for(int i =leftCenterIndex, j = rightCenterIndex; i >= 0 && j <= s.length() -1; i--, j++) {
                if(s.charAt(i) == s.charAt(j)) {
                     //说明出现了  一个回文串了
                    if( (j - i + 1) > tempLongestLength ) {
                        tempLongestLength = j - i + 1;
                    }
                } else {
                    //如果不相等,说明不对称  直接break了
                    break;
                }
            }
            return tempLongestLength;
            
        }
    }

     动态规划法:

    用一个二维数组来求解,dp[i][j]的值标识,字符串中从i到j是不是回文串。
    dp[i][j]=1,我们只要想办法把二维数组填好后,找出j - 1 + 1最大的值,求出子串序列返回即可。

    填二维数组的时候有几个情况:
      1.单个字符的时候,也就是dp的对角线i==j的时候,肯定为1;
      2.相邻字符的时候,也就是dp中j - i < 2的时候,只有两个相同才为1,即s.charAt(i)==s.charAt(j)时有dp[i][i + 1] = 1;
      3.字符相隔大于2的时候,也就是j - 1 >= 2的时候,这个时候值为1,需要边缘两个值相等,也就是s.charAt(i)==s.charAt(j);还有中间也是回文串——也即dp[i+1][j-1] == 1

    具体代码实现的话,分三轮遍历来

    首先有个二维数组,然后:

      1.第一轮遍历,单个字符是回文串,也就是dp[i][i] = 1;

      2.第二轮遍历,相邻字符是不是回文串呢?遍历看相邻字符是不是相等的,即s[i]和s[i + 1]相等的话则有dp[i][i + 1] = 1,即j - 1 == 1的情况;

      3.第三轮遍历,真正的动态规划,刚刚把数组dp中对角线上的还有dp[i][j]i和j间隔为1的也就j - 1 < 2的情况的空都填了,现在要做的是利用前面数组的结果,填dp数组中j - i >= 2的格子。第三轮遍历是个二重循环,j - i >=2时,进行遍历,然逐渐增大j和i的距离。只有s[i] == s[j],且dp[i + 1][j - 1]为1两者都成立的情况下,dp[i][j] = 1。

    算法Time:O(n^2), Space:O(n^2)。

    代码:(引用自——https://www.cnblogs.com/clnchanpin/p/6880322.html)

    public String longestPalindrome(String s) {
            if (s == null)
                return null;
         
            if(s.length() <=1)
                return s;
         
            int maxLen = 0;
            String longestStr = null;
         
            int length = s.length();
         
            int[][] table = new int[length][length];
         
            //every single letter is palindrome
            for (int i = 0; i < length; i++) {
                table[i][i] = 1;
                longestStr = s.charAt(i) + ""; //此时  最大的  回文串就是  单个字咯
                maxLen = 1;//最大回文串长度就为1咯
            }
            
        
            //two consecutive same letters are palindrome
            for (int i = 0; i <= length - 2; i++) {
               
                if (s.charAt(i) == s.charAt(i + 1)){//相隔的两个字相等
                    table[i][i + 1] = 1;
                    longestStr = s.substring(i, i + 2);
                    maxLen = 2;//两个相等的字符串嘛
                }    
            }
           
            //condition for calculate whole table
            //前面对于单个字也就是二维数组中对角线上的情况还有   间隔相等的情况  也就是j - i = 1的情况都处理了
            //这里是  对其他需要  借助动态规划也就是之前数组的结果来操作的情况  也就是  j - 1 >=2的情况
            for (int len = 3; len <= length; len++) {
                /*第一层循环   是以长度为基准的,因为这里处理的是j - i >=2的情况嘛,也就是比如abc,a和c这样的情况,也就是子串长度3以上的情况,所以第一层循环就描述这个,从3开始慢慢加
            *直到table[0][length - 1]填好就可以有结果了
            */
    for (int i = 0; i < length - len + 1; i++) { //这个条件是 因为len = j - 1 + 1,然后又有j < length得来的 (后面补充:)没那么复杂……就比如len是2,i是0,那么j的坐标就应该是i+2-1=1,所以就i+len-1<length即可
    int j = len + i - 1;//len = j - 1 + 1 if(s.charAt(i) == s.charAt(j) && table[i + 1][j - 1] == 1) { table[i][j] = 1; if(len > maxLen) { longestStr = s.substring(i, j + 1); } } else { table[i][j] = 0; } } } return longestStr; }

    还有个很出名的Manacher 算法,时间复杂度为O(n),具体的一时半会没看懂,以后需要再找资料吧~

     

  • 相关阅读:
    Blank page instead of the SharePoint Central Administration site
    BizTalk 2010 BAM Configure
    Use ODBA with Visio 2007
    Handling SOAP Exceptions in BizTalk Orchestrations
    BizTalk与WebMethods之间的EDI交换
    Append messages in BizTalk
    FTP protocol commands
    Using Dynamic Maps in BizTalk(From CodeProject)
    Synchronous To Asynchronous Flows Without An Orchestration的简单实现
    WSE3 and "Action for ultimate recipient is required but not present in the message."
  • 原文地址:https://www.cnblogs.com/wangshen31/p/10308813.html
Copyright © 2011-2022 走看看