1. 程式人生 > >hohor學習日記:hiho一下 第五十八週

hohor學習日記:hiho一下 第五十八週

http://hihocoder.com/contest/hiho58/problem/1

題意分析

給定字串S,判定S是否存在子串S’滿足"aa…abb…bcc…c"的形式。其中abc為連續的三個字母,且a,b,c的數量相同。

原題目中數量相等的連續n(n>3)個字母也是可行的,而實際上當n>3時一定包含有n=3的情況。比如"abcd"就包含有"abc"和"bcd"兩個合法子串。

演算法分析

最基本的思路為對S的每一個子串進行判定是否滿足要求。列舉子串的起點、終點以及檢查是否合法。

假設S的長度為N,則時間複雜度為O(N^3)。

For i = 0..N-
1 For j = 0..N-1 check(S[i..j]) End For End For

這樣的做法對於N稍大的資料來說就會超過時限。

進一步考慮,由於合法子串中相同的字母總是連續的,我們不妨用(c,n)來表示一串連續相同的字母,比如"aaa"表示(a,3),"bb"表示為(b,2)。

我們將整個字串S用(c,n)表示,得到{(c[1], n[1]),(c[2],n[2]),…,(c[t],n[t])}的序列。其中我們合法的子串也可以表示為{(a,n),(b,n),(c,n)}。

則演算法改變為在序列{(c[1], n[1]),(c[2],n[2]),…,(c[t],n[t])}中判定是否存在連續的3個元素滿足c[i],c[i+1],c[i+2]連續且n[i] == n[i+1] == n[i+2]。

預處理時間為O(N),得到的序列長度最大為N,所以整體的時間複雜度降低為O(N)。

For i = 1 .. t-2
    If (c[i]+1 == c[i+1] and c[i+1]+1 == c[i+2]) and (n[i] == n[i+1] == n[i+2])
        Return True
    End If
End For

然而實際執行會發現,這個演算法是不正確的。比如:“aaaabbccc”,其對應的序列為{(a,4),(b,2),(c,3)},根據我們上面的演算法並不能找到合法子串。但實際上存在合法子串"aabbcc"。

很顯然,問題出在我們對於n[i],n[i+1],n[i+2]的判定上。通過上面的反例我們可以發現,在子串中n[i],n[i+2]的值其實是可以變動的,唯一固定的是n[i+1]的值。當n[i]>n[i+1]時,我們只要刪去前面的若干個字母,就能夠使得n[i]==n[i+1]。同理對於n[i+2]>n[i+1]時,我們刪去後面的字母。因此只要有n[i]>=n[i+1],n[i+2]>=n[i+1],就一定能夠通過變換使得n[i] == n[i+1] == n[i+2]。

改正後我們的演算法程式碼為:

For i = 1 .. t-2
    If (c[i]+1 == c[i+1] and c[i+1]+1 == c[i+2]) and (n[i] >= n[i+1] and n[i+1] <= n[i+2])
        Return True
    End If
End For

結果分析

在實際的比賽中,該題目的通過率僅為26%。

但根據賽後的統計結果,大部分的選手都使用了樸素的演算法通過了規模較小的資料點。在該題目上獲取了10~60不等的分數。

其中比較有意思的是有一位選手僅僅判定連續3個字母是否連續,也獲得了60的分數。

而分佈在70~90分數段的程式,隨機抽取了若干樣本,發現大多數都是想到了正確演算法的。而導致他們丟分的主要原因則是多組資料產生的初始化問題。

AC程式碼:

#include <bits/stdc++.h>

using namespace std;
#define LL long long
const int Mod = 1e9 + 7;
const int maxn = 1e7 + 5;
const double eps = 0.00000001;
const int INF = 0x3f3f3f3f;

int c[maxn], m[maxn];

bool solve() {
    LL n;
    memset(c, 0, sizeof(c));
    memset(m, 0, sizeof(m));
    cin >> n;
    string ss;
    cin >> ss;
    int cnt = 0;
    c[0] = ss[0];
    m[0] = 1;
    for (LL i = 1; i < n; i ++) {
        if(ss[i] != ss[i - 1]) {
            cnt ++;
            c[cnt] = ss[i];
        }
        m[cnt]++;
    }
    for (LL i = 1; i <= cnt - 1; i ++)
        if(c[i - 1] + 1 == c[i] && c[i] + 1 == c[i + 1] && m[i - 1] >= m[i] && m[i] <= m[i + 1]) return true;
    return false;
}

int main()
{
    int T;
    cin >> T;
    while(T--) {
        if(solve()) cout << "YES\n";
        else cout << "NO\n";
    }
    return 0;
}