最长回文子串

问题描述

回文串是指这个字符串无论从左读还是从右读,所读的顺序是一样的;简而言之,回文串是左右对称的。现在,对于一个给定的母串

abcdedcb

可以找出子串a, ded, cdedc, bcdecdb等均是回文串;显然,bcdecdb是其中最长的那一个。但是该如何找出最长的回文子串呢?

问题解法

最容易想到的依然是穷举法,穷举所有子串,找出是回文串的子串,统计出最长的那一个。

穷举法

i, j的for循环表示字符串s[i..j],然后逐一判断,找出最长。

/*判断str[i..j]是否为回文串*/
int isPalindrome(char *str, int begin, int end) {
    while(str[begin] == str[end] && begin <= end) {
        begin++;
        end--;
    }
    if(str[begin] != str[end]) {
        return 0;
    } else {
        return 1;
    }
}

/*返回最长回文子串长度*/
int longestPald(char *str) {
    int len = strlen(str);
    int i, j;
    int longest = 1;
    assert(str != NULL);
    if(len == 1) {
        return 1;
    }

    for(i = 0; i<len; i++) {
        for(j=i+1; j<len; j++) {
            if(isPalindrome(str, i, j)) {
                longest = (longest < j-i+1 ? j-i+1: longest);
            }
        }   
    }
    return longest;
}

穷举法的时间复杂度为\(O(n^3)\),复杂度太高了,需要做一些改进。

对穷举法的改进

回文串是左右对称的,如果从中心轴开始遍历,会减少一层循环。思路:依次以母串的每一个字符为中心轴,得到回文串;然后通过比较得到最长的那一个。注意:要考虑到像baccab这样的特殊子串,可以理解它的中心轴是cc。

/*以mid为中心轴的回文子串*/
int l2r(char *str, int mid) {
    int l = mid -1, r = mid+1;
    int len = strlen(str);
    while(str[r] == str[mid]) {
        r++;
    }
    while(l >= 0 && r < len && str[l] == str[r]) {
        l--;
        r++;
    }
    return r-l-1;
}

int longestPald(char *str) {
    int len = strlen(str);
    int i;
    int longest = 1;
    assert(str != NULL);
    if(len == 1) {
        return 1;
    }

    for(i = 0; i<len; i++) {
        if(l2r(str, i) > longest)
            longest = l2r(str, i);
    }

    return longest;
}
  • 时间复杂度:\(O(n^2)\).
  • 空间复杂度:\(O(1)\).

动态规划

有母串s,我们用c[i, j] = 1表示子串s[i..j]为回文子串,那么就有递推式

\[c[i,j] = \left\{ {\matrix{ c[i+1, j-1] & {if \ s[i] = s[j]} \cr 0 & {if \ s[i] \ne s[j]} \cr } } \right.\]

递推式表示在s[i] = s[j]情况下,如果s[i+1..j-1]是回文子串,则s[i..j]也是回文子串;如果s[i+1..j-1]不是回文子串,则s[i..j]也不是回文子串。

初始状态:

  • c[i][i] = 1
  • c[i][i+1] = 1 if s[i] == s[i+1]

上述式子表示单个字符、两个字符均是回文串。

int longestPald(char *str) {
    int len = strlen(str);
    int c[maxLen][maxLen];
    int i,j;
    int longest = 1;
    
    assert(str != NULL);
    if(len == 1) {
        return 1;
    }
      //initialization
    for(i = 0; i < len; i++) {
        c[i][i] = 1;
                if(str[i] == str[i+1])
           c[i][i+1] = 1;
    }

    for(i = 0; i < len; i++) {
        for(j = i+2; j <= len; j++) {
            if(str[i] == str[j]) {
                c[i][j] = c[i+1][j-1];
                //find longest palindrome substring
                if(c[i][j]) { 
                    int n = j - i + 1;
                    if(longest < n)
                        longest = n;                
                }
            } else {
                c[i][j] = 0;
            }
        }
    }
    return longest;
}
  • 时间复杂度:\(O(n^2)\).
  • 空间复杂度:\(O(n^2)\).

你可能感兴趣的:(最长回文子串)