P4824 [USACO15FEB] Censoring S

Captainfly19發表於2024-06-10

題目連結:https://www.luogu.com.cn/problem/P4824

kmp+棧

棧處理字串問題有一道入門題:https://www.luogu.com.cn/problem/AT_abc328_d
實際上處理方式就是用陣列模擬棧.在遍歷字串的過程中我們時刻監測,對未達到條件的字元我們進行壓棧操作,然後如果說遇到了符合條件的字元就減去棧中的這一部分,然後對後面的字元繼續重複上述操作.最後只需要輸出一整個棧就行.

abc328d的參考程式碼:

#define maxn 200010
char stk[maxn];
int tp;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    string s;
    cin>>s;
    for(int i=0;i<s.size();i++)
    {
        stk[++tp]=s[i];
        if(stk[tp]=='C'&&stk[tp-1]=='B'&&stk[tp-2]=='A')
        {
            tp-=3;
        }
    }
    for(int i=1;i<=tp;i++)
    {
        cout<<stk[i];
    }
}

那麼有了上一道題的經驗,在這道題中我們也可以用相同的方式去監測和刪除.
只不過這題需要刪除的子串是一個長度範圍為1-1e6的字串,如果繼續用上面這種暴力的監測方式時間複雜度最壞會達到 \(O(n^{2})\)
在這裡我們考慮把監測方式從暴力改為kmp.運用kmp的匹配操作完成監測,可以把時間複雜度最佳化成線性的.
棧發揮的作用和上一道題基本一致,由於刪除操作會導致單個字元左右相鄰的字元的變動.所以我們需要使用一個pos陣列,去記錄在匹配時,文字串中每個位置在模式串中對應的位置.這樣能在刪除操作結束時更新模式串的指標位置.

#define maxn 1000010
string s,t;
int stk[maxn],top,pos[maxn];
vector<int> get_nxt(string s)
{
    int n=s.size();
    vector<int> nxt(n);
    for(int i=1,j=0;i<n;i++)
    {
        while(j&&s[i]!=s[j])
        {
            j=nxt[j-1];
        }
        j+=(s[i]==s[j]);
        nxt[i]=j;
    }
    return nxt;
}
void kmp(const string s,const string t)
{
    vector<int> nxt=get_nxt(t);
    for(int i=0,j=0;i<s.size();i++)
    {
        while(j&&s[i]!=t[j]) j=nxt[j-1];
        if(s[i]==t[j]) j++;
        pos[i]=j;
        stk[++top]=i;
        if(j==t.size())
        {
            top-=(int)(t.size());
            j=pos[stk[top]];
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>s>>t;
    kmp(s,t);
    for(int i=1;i<=top;i++)
    {
        cout<<s[stk[i]];
    }
}