【字符串】B056_LC_切开字符串(暴力取子串 / 反向思维+累加思想)

Pear有一个字符串,不过他希望把它切成两段。这是一个长度为N(<=10^5)的字符串。Pear希望选择一个位置,把字符串不重复不遗漏地切成两段,长度分别是t和N-t(这两段都必须非空)。

Pear用如下方式评估切割的方案:

  • 定义“正回文子串”为:长度为奇数的回文子串。
  • 设切成的两段字符串中,前一段中有A个不相同的正回文子串,后一段中有B个不相同的非正回文子串,则该方案的得分为A*B。

注意,后一段中的B表示的是:“…非正回文…”,而不是: “…正回文…”。
那么所有的切割方案中,A*B的最大值是多少呢?

输入数据
输入第一行一个正整数N(<=10^5)
接下来一行一个字符串,长度为N。该字符串仅包含小写英文字母。
输出数据
一行一个正整数,表示所求的A*B的最大值。

【样例输入】
10
bbaaabcaba
【样例输出】
38

【数据范围】
对于20%的数据,N<=100
对于40%的数据,N<=1000
对于100%的数据,N<=10^5

方法一:暴力检查

每次都去检查两段子串 正回文 s[0:i] 和 非正回文 s[i:] 的个数

#include
using namespace std;

bool chk(string& s) {
    int n=s.size(), l=0, r=n-1;
    if (n%2==0) return false;
    for (int i=0; i<n/2; i++) if (s[i] != s[n-i-1])
        return false;
    return true;
}
int l_cnt(string& s) {
    int n=s.size();
    unordered_set<string> st1;
    for (int i=0; i<n; i++) 
    for (int len=1; len<=n-i; len++) {
        string t=s.substr(i, len);
        if (chk(t)) st1.insert(t);
    }
    return st1.size();
}
int r_cnt(string& s) {
    int n=s.size();
    unordered_set<string> st2;
    for (int i=0; i<n; i++) 
    for (int len=1; len<=n-i; len++) {
        string t=s.substr(i, len);
        if (!chk(t)) st2.insert(t);
    }
    return st2.size();
}
int n;
string s;
int main() {
    std::ios::sync_with_stdio(false);
    cin >> n >> s;
    long ans=0;
    for (int i=1; i<=n; i++) {
        string l=s.substr(0, i), r=s.substr(i, n-i);
        ans=max(ans, (long) l_cnt(l)*(long) r_cnt(r));
    }
    cout << ans;
    return 0;
}

复杂度分析

  • Time O ( n 4 ) O(n^4) O(n4)
  • Space O ( n ) O(n) O(n)

方法二:累加思想

思路

反着来,例如求正回文的时候:固定一个末端点 r,然后从 r 到 0 枚举一个起始端点 l,求出 s[l:r] 的正回文子串存到 set 中,等 r++ 后,继续这样求,且将正回文的个数累加起来

#include
using namespace std;

bool chk(string& s) {
    int n=s.size();
    if (n%2==0) return false;
    for (int i=0; i<n/2; i++) if (s[i] != s[n-i-1])
        return false;
    return true;
}
int main() {
    std::ios::sync_with_stdio(false);
    int n;
    string s;
    cin >> n >> s;
    int ans=0, l_cnt[n], r_cnt[n];

    unordered_set<string> st1, st2;
    for (int r=0; r<n; r++) {
        for (int l=r; l>=0; l-=2) {	//减2的原因是避免取出来的子串冗余,因为每次l都是从右往左的
            int len=r-l+1;
            string t=s.substr(l, len);
            if (chk(t)) st1.insert(t);
        }
        l_cnt[r]=st1.size();
    }
    for (int l=n-1; l>=0; l--) {
        for (int r=l; r<n; r++) {	//这里取出来的子串都是新的,r正常+1
            int len=r-l+1;
            string t=s.substr(l, len);
            if (!chk(t)) st2.insert(t);
        }
        r_cnt[l]=st2.size();
    }
    for (int i=1; i<n; i++) ans=max(ans, l_cnt[i-1]*r_cnt[i]);
    cout << ans;
    return 0;
}

复杂度分析

  • Time O ( n 2 ) O(n^2) O(n2)
  • Space O ( n ) O(n) O(n)

你可能感兴趣的:(#,字符串)