美文网首页
LeetCode第5题:longestPalindrome(C语

LeetCode第5题:longestPalindrome(C语

作者: 闫品品 | 来源:发表于2019-05-27 14:39 被阅读0次

    上一题:LeetCode第4题:findMedianSortedArrays(C语言)
    写在前面,最长公共字符串是面试中较常出现的题目,原因是这道题解法众多,可对面试者进行综合考察。

    1、暴力求解
    思路:双层遍历输入字符串,从而穷举出输入字符串的所有子串,然后再用一层循环,判断子串是否为回文字符串,思路清晰简单,代码略。

    2、动态规划
    思路:
    假设字符串s的长度为length,建立一个length*length的矩阵dp。
    dp[i][j]表示“以s[i]开始s[j]结尾的回文串的长度。如果这个字符串不是回文串,让dp[i][j]=0”。显然,j>=i,只需往dp填j>=i的部分即可。
    dp[i][j]的递推公式可以这么表述:
    (1)首先对dp的对角线元素初始化为1,也就是当i==j时,dp[i][j]=1。
    这很显然,每个单独的字符其实就是个长度为1的回文串。
    (2)当j-i==1:
    若s[i]==s[j],则dp[i][j]=2;否则dp[i][j]=0。
    解释:当j-i==1时,若s[i]==s[j],则s[i]和s[j]可以组成一个长度为2的回文串。若s[i]!=s[j],显然他们不可能组成回文串,dp[i][j]=0。
    (3)当j-i>=2:
    若s[i]==s[j]:若dp[i+1][j-1]>0,则dp[i][j]= dp[i + 1][j - 1] + 2;否则dp[i][j]= 0;
    若s[i]!=s[j]:dp[i][j]=0。
    解释:如果s[i]==s[j],表明这个子串有可能是回文串。当去头去尾后它是回文串时,就可以在去头去尾的那个回文串长度基础上+2,得到它的长度。如果去头去尾后不是回文串,那这个子串一定不是回文串,回文串长度只能是0。
    若s[i]!=s[j],显然他们不可能组成回文串,dp[i][j]=0。
    只需找到dp[i][j]的最大元素和它对应的i和j就可以得到结果“s中最长回文子串”。
    另外还有一个要注意的点:因为需要访问dp[i+1][j-1],因此i是从大到小的,j是从小到大的。j从0到size-1,i从j-1到0。

    char * longestPalindrome(char * s){
        int len = strlen(s);
        if(len <= 1) return s;
        
        int dp[len][len];
        for(int i = 0; i < len; i++){
            for(int j = 0; j < len; j++){
                dp[i][j] = i == j;
            }
        }
        
        int start = 0;
        int max = 1;
        for(int j = 1; j < len; j++){
            for(int i = j - 1; i >= 0; i--){
                if(s[i] == s[j]){
                    if(j - i == 1) 
                        dp[i][j] = 2;
                    else{
                        if(dp[i + 1][j - 1] > 0)
                            dp[i][j] = dp[i + 1][j - 1] + 2;
                        else
                            dp[i][j] = 0;
                    } 
                }
                else
                    dp[i][j] = 0;
                if(dp[i][j] >= max) {
                    max = dp[i][j]; 
                    start = i;
                }
            }
        }
        
        char *result = (char *)malloc((max + 1) * sizeof(char));
        for(int i = 0;i < max; i++){
            result[i] = s[start + i];
        }
        result[max] = '\0';
        
        return result;
    }
    
    

    3、中心扩散法
    思路:遍历输入的字符串s,以每个字符mid为中心,分别计算mid为中心的奇数位(例如"cabac"中以"b"为中心的回文字符串)和偶数位(例如"cabbac"以"bb"为中心的回文字符串)的最长回文字符串,记录最长回文字符串出现的起始下标及长度max。

    char * longestPalindrome(char * s){
        int len = strlen(s);
        int start = 0;
        int mid = 0;
        int max = 0;
        int extend = 0;
        while(mid < len){
            //计算形如 "cabbac"以"bb"为中心的回文字符串
            extend = 0;
            while(mid - extend >= 0 && mid + extend + 1 < len && s[mid - extend] == s[mid + extend + 1]){
                extend++;
            }
            if(2 * extend >= max){
                max = 2 * extend;
                start = mid - extend + 1;
            }
            //计算形如 "cabac"中以"b"为中心的回文字符串
            extend = 0;
            while(mid - extend - 1 >= 0 && mid + extend + 1 < len && s[mid - extend - 1] == s[mid + extend + 1]){
                extend++;
            }
            if(2 * extend + 1 >= max){
                max = 2 * extend + 1;
                start = mid - extend;
            }
            mid++;
        }
        
        char *result = (char *)malloc((max + 1) * sizeof(char));
        for(int i = 0;i < max; i++){
            result[i] = s[start + i];
        }
        result[max] = '\0';
        
        return result;
    }
    

    4、中心扩散的改进算法
    思路:中心扩散思路中,需要计算mid为中心的奇数位(例如"cabac"中以"b"为中心的回文字符串)和偶数位(例如"cabbac"以"bb"为中心的回文字符串)的最长回文字符串,现将输入的字符串左右填充特殊字符,即将输入字符"babacd"每两个字符之间分别填充字符"",变为"babbad",可将所有字符均变为奇数个长度,从而避免了奇数位和偶数位分别判断,但该方法需要额外申请内存存储填充字符串,时间上并没有快,但可为后续时间复杂度为O(n)的方法提供思路。

    char * longestPalindrome(char * s){
        int len = strlen(s);
        int new_len = 2 * len + 1;
        char *new_s = (char *)malloc((new_len + 1) * sizeof(char));
        int j = 0;
        
        for(int i = 0; i < len; i++){
            new_s[j++] = '*';
            new_s[j++] = s[i];
        }
        new_s[j] = '*';
        new_s[new_len] = '\0';
    
        int start = 0;
        int mid = 1;
        int max = 0;
        int extend = 0;
        while(mid < new_len){
            extend = 0;
            while(mid - extend - 1 >= 0 && mid + extend + 1 < new_len && new_s[mid - extend - 1] == new_s[mid + extend + 1]){
                extend++;
            }
            if(2 * extend + 1 >= max){
                max = 2 * extend + 1;
                start = mid - extend;               
            }
            mid += 1;
        }
        
        char *result = (char *)malloc((max / 2 + 1) * sizeof(char));
        for(int i = 0; i < max / 2; i++){
            result[i] = new_s[start + 1 + 2 * i ];
        }
        result[max / 2] = '\0';
        
        return result;
    }
    

    本系列文章,旨在打造LeetCode题目解题方法,帮助和引导同学们开阔学习算法思路,由于个人能力和精力的局限性,也会参考其他网站的代码和思路,如有侵权,请联系本人删除。

    下一题:LeetCode第6题:zigzag-conversion(C语言)

    相关文章

      网友评论

          本文标题:LeetCode第5题:longestPalindrome(C语

          本文链接:https://www.haomeiwen.com/subject/pqrhtctx.html