1. 程式人生 > >百度2014研發類校園招聘筆試題解答

百度2014研發類校園招聘筆試題解答

演算法2:採用動態規劃法判斷子串是否是迴文。開闢一個P[i][j]用來表示str[i..j]是否為迴文,P[i][j]的狀態轉移方程如下:
  1. 當i==j時,P[i][j]=true

  2. 當i+1==j時,P[i][j]=str[i]==str[j]

  3. 其他,P[i][j]=P[i+1][j-1]&&(str[i]==str[j])

那麼P[i][j]中j-i+1最大的且值為true的就是最長迴文子串。這樣,這個方法的時間複雜度為O(n^2),空間複雜度為O(n^2)。比暴力法有很大的改進。

Source Code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
longestPalSubstr(char *str) { int n = strlen(str); int i, j, len, maxlen = 0, maxi = 0, maxj = 0; bool **P = (bool**)malloc(sizeof(bool) * n); for(i = 0; i < n; i++) { P[i] = (bool*)malloc(sizeof(bool) * n); } // initialize P[i][i] for(i = 0; i < n; i++) { P[i][i]
= true; } // compute P[n][n] by length for(len = 2; len <= n; len++) { for(i = 0; i < n - len + 1; i++) { j = i + len - 1; if(len == 2) { P[i][j] = (str[i] == str[j]); } else { P[i][j]
= ((str[i] == str[j]) && P[i + 1][j - 1]); } } } // int k; for(i = 0; i < n; i++) { for(j = i; j < n; j++) { // printf("%d ", P[i][j]); if(P[i][j] && maxlen < (j - i + 1)) { maxlen = j - i + 1; maxi = i; maxj = j; } } // printf("\n"); // for(k = 0; k <= i; k++) // printf(" "); } printf("The longest palin substr is "); for(i = maxi; i <= maxj; i++) { printf("%c", str[i]); } printf(", maxlen is %d\n\n", maxlen); return maxlen; } int main() { char str[100]; while(1) { gets(str); if(strlen(str) == 0) break; longestPalSubstr(str); } return 0; }

演算法3:第三個方法,可以從上面那個方法的狀態轉移方程獲得啟發,對於每一個迴文子串可以先確定一箇中心,然後向兩邊擴充套件,這樣可以在時間複雜度O(n^2),空間複雜度O(1)的情況下完成,需要注意的是,長度為奇數和偶數的中心的情況是不同的。

Source Code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int longestPalSubstr(char *str)
{
    int len = strlen(str);
    int i, maxLen = 1, start = 0;
    int low, high;
    
    // 將每個字元作為中心向兩邊擴充套件判斷
    for(i = 1; i < len; i++)
    {
        // 處理長度為偶數的情況
        low = i - 1;
        high = i;
        while(low >= 0 && high < len && str[low] == str[high])
        {
            if(maxLen < high - low + 1)
            {
                start = low;
                maxLen = high - low + 1;
            }
            low--;
            high++;
        }

        // 處理長度為奇數的情況
        low = i - 1; 
        high = i + 1;
        while(low >= 0 && high < len && str[low] == str[high])
        {
            if(maxLen < high - low + 1)
            {
                start = low;
                maxLen = high - low + 1;
            }
            low--;
            high++;
        }
    }

    printf("The longest palin substr is ");
    for(i = start; i < start + maxLen; i++)
    {
        printf("%c", str[i]);
    }
    printf(", maxlen is %d\n\n", maxLen);

    return maxLen;
}

int main()
{
    char str[100];
    
    while(1)
    {
        gets(str);
        if(strlen(str) == 0) break;
        longestPalSubstr(str);
    }

    return 0;
}

演算法4:第四個方法採用字尾陣列,將最長迴文子串的問題轉化為最長公共字首的問題。具體的做法就是:將整個字串翻轉之後,拼接到原字串後,注意用特殊字 符分開,這樣問題就變成了新的字串的某兩個字尾的最長公共字首的問題了。這個方法比較強大,很多字串的問題都能夠巧妙的解決。不過實現起來也相對比較:難,好的實現和差的實現時間複雜度相差很大。由於對字尾陣列不是很清楚,未寫程式碼,等學習了字尾陣列再過來補。

演算法5:第五個方法叫做Manacher演算法,是一種線性時間的方法,非常巧妙。首先,我們在上面的方法中個,都要考慮迴文長度為奇數或者偶數的情況。這個:方法,引入一個技巧,使得奇數和偶數的情況統一處理了。具體做法如下:

abba轉換為#a#b#b#a#,也就是在每一個字元兩邊都加上一個特殊字元。

然後建立一個新的P[i]表示,以第i個字元為中心的迴文字串的半徑。例如上面的例子,對應的P如下,設S為原始字串:

S # a # b # b # a #
P 1 2 1 2 5 2 1 2 1

通過觀察上面的表,大家可以發現P[i]-1就是實際迴文字串的長度。如果知道P,遍歷一次就知道最長的迴文子串。可以該如何計算P呢?這是這個演算法最核心的部分。

下面的討論基本轉自部落格:http://www.felix021.com/blog/read.php?2040 該部落格中對Manacher演算法介紹得也非常好,向大家推薦。

演算法引入兩個變數id和mx,id表示最長迴文子串的中心位置,mx表示最長迴文字串的邊界位置,即:mx=id+P[id]。

在這裡有一個非常有用而且神奇的結論:如果mx > i,那麼P[i] >= MIN(P[2 * id - i], mx - i) 分開理解就是:

  1. 如果mx - i > P[j], 則P[i]=P[j]

  2. 否則,P[i]  = mx - i.

這兩個該如何理解呢?具體的解釋請看下面的兩個圖。

(1)當 mx - i > P[j] 的時候,以S[j]為中心的迴文子串包含在以S[id]為中心的迴文子串中,由於 i 和 j 對稱,以S[i]為中心的迴文子串必然包含在以S[id]為中心的迴文子串中,所以必有 P[i] = P[j],見下圖。

(2)當 P[j] >= mx - i 的時候,以S[j]為中心的迴文子串不一定完全包含於以S[id]為中心的迴文子串中,但是基於對稱性可知,下圖中兩個綠框所包圍的部分是相同的,也就是 說以S[i]為中心的迴文子串,其向右至少會擴張到mx的位置,也就是說 P[i] >= mx - i。至於mx之後的部分是否對稱,就只能老老實實去匹配了。

對於 mx <= i 的情況,無法對 P[i]做更多的假設,只能P[i] = 1,然後再去匹配了。

理解了上面的一點,就沒有問題了。

Source Code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int longestPalSubstr(char *str)
{
    char s[100];
    int i, maxLen = 1, start = 0, j;
    int len = strlen(str);
    int mx = 0, id = 0, min;

    s[0] = '$';
    s[1] = '#';
    for(i = 0, j = 2; i < len; i++, j += 2)
    {
        s[j] = str[i];
        s[j + 1] = '#';
    }
    s[j] = '\0';

    len = len * 2 + 1;
    int *p = (int *)malloc(sizeof(int) * len);
    memset(p, 0, len);
    p[0] = 1;

    for(i = 1; i < len; i++)
    {
        min = p[2 * id - i] > (mx - i) ? (mx - i) : p[2 * id - i];
        p[i] = mx > i ? min : 1;
        while(s[i + p[i]] == s[i - p[i]])
        {
            p[i]++;
        }
        if(i + p[i] > mx)
        {
            mx = i + p[i];
            id = i;
        }
    }

    for(i = 0; i < len; i++)
    {
        //printf("%d ", p[i]);
        if(maxLen < p[i] - 1)
        {
            maxLen = p[i] - 1;
            start = i - maxLen;
        }
    }

    printf("The longest palin substr is ");
    for(i = start; i < start + 2 * maxLen + 1; i++)
    {
        if(s[i] != '#')
        {
            printf("%c", s[i]);
        }
    }
    printf(", maxlen is %d\n\n", maxLen);

    return maxLen;
}

int main()
{
    char str[100];
    
    while(1)
    {
        gets(str);
        if(strlen(str) == 0) break;
        longestPalSubstr(str);
    }

    return 0;
}