PAT1040 Longest Symmetric String (25分) 中心擴充套件法+動態規劃

無程式碼,非程式發表於2020-06-10

題目

Given a string, you are supposed to output the length of the longest symmetric sub-string. For example, given Is PAT&TAP symmetric?, the longest symmetric sub-string is s PAT&TAP s, hence you must output 11.

Input Specification:
Each input file contains one test case which gives a non-empty string of length no more than 1000.

Output Specification:
For each test case, simply print the maximum length in a line.

Sample Input:

Is PAT&TAP symmetric?

Sample Output:

11

題目解析

給定一個字串,要求輸出它最長迴文子串的長度。

什麼是迴文子串,就是類似 baab aacaa這種中心對稱的字串。

注意,輸入字串可能包括空格,所以這裡使用getline(cin,str)

思路一:中心擴充套件法

所謂中心擴充套件法,就是從迴文串“中心對稱”這個特點來的。

我們先分析一下這個“對稱”,如果是奇數長度的字串,那麼它關於最中心的那個字元對稱;如果是偶數長度的字串,它的對稱線是最中心兩個字元的中間畫一條線(比如baab),也就是關於最中心兩個字元(aa)是對稱的(那兩個字元是一樣的)

所以中心擴充套件法的思路就是,把某個位置作為中間位置,向兩邊擴充套件,直到左右指標對應位置字元不等

那麼對於一個字串,中心位置如何取,如果以每個字元作為中心,那麼我們就能找到它所有長度為奇數的最長對稱串的長度,以連續兩個字元作為中心,救能得到所有長度為偶數的最長的對稱串的長度,然後我們再二者之間取最大值即可

文字描述比較抽象,直接看程式碼,挺容易理解的。

#include <iostream>
using namespace std;

// 中心擴充套件法
int helper(string s, int leftborder,int l,int r,int rightborder) {
    // 向兩端無限擴充套件
    while(leftborder <= l && s[l] == s[r] && r <= rightborder) {
        --l;++r;
    }
    // 已記錄的有效迴文串長度
    return r - l - 1;
}
int main() {
    string s;
    getline(cin, s);
    int len = s.length();
    int res = 0;
    for (int i = 0; i < len; ++i) {
        // 以本身為中心,像左右擴充套件
        int len1 = helper(s, 0, i, i, len - 1);
        // 以自己和下一個字元為中心,向左右擴充套件
        int len2 = helper(s, 0, i, i + 1, len - 1);
        res = max(res, len1);
        // 總是取更大那個
        res = max(res, len2);
    }
    cout << res;
}

思路二:動態規劃

思路一里面對於每個字元都要進行兩次中心擴充套件,肯定進行了很多次重複操作,而動態規劃就是為解決重複操作而生的。

把一個字串表示為 s[0],s[1]...s[i],s[i+1],s[i+2]...s[j-2],s[j-1],s[j]...s[len-1]

  • 如果 s[i+1,j-1] 是迴文串,那麼只要 s[i] == s[j],就可以確定 s[i][j] 也是迴文串
  • 長度為12時的子串需單獨判斷
  • dp[i][j] 代表 s[i][j] 是不是迴文子串

動態規劃的核心就是由子問題狀態保留,不再重新計算,對於一個長度為len的字串,它的每個子串長度可以是 1到len,我們從小到大取出所有長度的子串進行判斷。


#include<iostream>
using namespace std;

int main() {
    string s;
    getline(cin, s);
    int len = s.length();
    int res = 0;
    bool dp[len][len] = {false};
    int maxLen = 0;
    //對於所有長度的子串
    for (int len = 1; len <= s.length(); len++) 
        for (int i = 0; i < s.length(); i++) {
            int j = i + len - 1; // i是起點,j是終點,長度是len
            // 當前情況不可能,不存在從i開始長為len的子串
            if (j >= s.length()) break; 
            //長度是1就是單個字元,滿足迴文
            if (len == 1) dp[i][j] = true; 
            // 長度是2就看這兩個字元是否相等
            else if (len == 2) dp[i][j] = s[i] == s[j];
            // 否則,如果 S[i+1,j-1] 是迴文串,只要 S[i] == S[j],S[i][j]也是迴文串
            else dp[i][j] = dp[i + 1][j - 1] && s[i] == s[j];
            // 當前串是迴文串且比上一次的更長
            if (dp[i][j] && len > maxLen) { 
                maxLen = len;
            }
        }
    cout << maxLen;
    return 0;
}

感覺動態規劃會比中心擴充套件更快,但提交結果是中心擴充套件更快,真是腦殼痛。。

相關文章