奇怪的字符串

ISIJ 2018 奇怪的字符串(Training Round D6T1)

無憂公主 2018-07-10

題目描述

考慮字符串 s 僅由小寫字母組成,例如 “abba”。定義 W(s) 爲 s 所有本質不同的連續子串的集合,例如 W(“abba”) = { “a”,”b”,”ab”,”ba”,”bb”,”abb”,”bba”,”abba” }。定義 Y(s) 爲 s 所有本質不同的非連續子串的集合,例如 Y(“abba”) = W(“abba”) ∪ { “aa”,”aba” },顯然 W(s) 是 Y(s) 的子集。

對於一些奇怪的字符串 s 滿足 W(s) = Y(s),例如 “abba” 就不是奇怪的,但 “abb” 是奇怪的因爲 W(s) = Y(s) = { “a”,”b”,”ab”,”bb”,”abb” }。現在小明有一個字符串 s,請你求出 W(s) 中有多少個字符串是奇怪的?

注意:集合中的所有元素互不相同

限制

1s 256M

1≤|s|≤ 200,000

輸入格式

一個字符串 s

輸出格式

一個整數,表示 W(s) 中有多少個字符串是奇怪的

輸入樣例

abba

輸出樣例

7

樣例解釋

abba 的所有連續子串中,除了 abba 以外都是 ” 奇怪的 “

題解

通過觀察可以發現,s 是 ” 奇怪的 ” 的條件是形如 aaaa 或 aaabbbb。對於連續的一段字母進行 “ 壓縮 ” 掃描線處理,記錄同種字母 k 的最大連續長度、字母 k1 在固定長度 c1 後面接字母 k2 的最大長度,以將本質不同(這是最麻煩的地方)的字符串區分開來。這些均可用數組和 map 維護(容易 MLE),並最後一邊維護最大值一邊統計答案。

掃描線學習

#include <bits/stdc++.h>
using namespace std;
template <typename T> void read(T &t) {
    char ch=getchar(); int f=1; t=0;
    while ('0'>ch||ch>'9') { if (ch=='-') f=-1; ch=getchar(); }
    do { (t*=10)+=ch-'0'; ch=getchar(); } while ('0'<=ch&&ch<='9'); t*=f;
}
typedef long long ll;
const int maxn=200010;
int n,c1,c2,a[30];
ll ans;
map<pair<int,int>,int> b[30];
struct node {
    int k1,k2,c1,c2;
} d[maxn];
char s[maxn],k1,k2;
int main() {
    scanf("%s",s+1); n=strlen(s+1);
    int pos=0,cnt=0; s[0]=s[1];
    for (int i=1;i<=n;i++) {
        while (pos<n) {
            if (s[pos]!=s[pos+1]&&cnt==1) break;
            if (s[pos]!=s[pos+1]) {
                cnt++;
                k2=s[pos+1]; c2++;
            } else if (c2) c2++; else k1=s[pos],c1++;
            pos++;
        }
        d[i]=(node){k1-'a'+1,k2-'a'+1,c1,c2};
        a[k1-'a'+1]=max(a[k1-'a'+1],c1);
        if (s[i]!=s[i+1]) {
            cnt--;
            k1=k2; c1=c2; c2=0;
        } else c1--;
    }
    for (int i=1;i<=26;i++)
        ans+=a[i];
    for (int i=1;i<=n;i++) {
        if (d[i].c2==0) continue;
        int tmp=b[d[i].k1][make_pair(d[i].c1,d[i].k2)];
        b[d[i].k1][make_pair(d[i].c1,d[i].k2)]=max(tmp,d[i].c2);
        ans+=max(0,d[i].c2-tmp);
    }
    printf("%lld\n",ans);
    return 0;
}

但是,比如我這種蠢笨的,肯定是看不出來是矩形面積並的
所以
有一種新的辦法

譬如我們先遇到了 aabb
然後我們遇到了新的 aaabbb
那麼 aabb 肯定是被包含在了 aaabbb 中的,所以我們可以用 aaabbb 覆蓋前面那個

但關鍵處理是
如果我們遇見了新的一個
aaaabb
怎麼搞呢
很容易發現新增的方案就是,a多出的長度(相較於aaabbb)乘以aaaabb中b的長度,也就是說,多了四個a與兩個b匹配的方案
但是問題又來了
譬如說
abbbb
aaaab
aaaabbbb
這個就有很難弄了,因爲如果按照原來的方案計算就會發生重複
所以我們把所有的字符串記錄下來,相同的字母按左右都以長度從大到小排序
那麼左邊從大到小排,後面就影響不到前面的了,我們只要比較右邊大小的差值
即可

#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct node{
    int pre,nex;
    bool operator < (const node x) const{
        if(pre==x.pre) return nex>x.nex;
        return pre>x.pre;
    }
};
vector<node> mp[26][26];
vector<int> vv;
vector<char> cc;
//char s[200000];
string s;
ll num[200000],l,vis[26],cnt,ans;

int check(int b,int x,int y){
    return x*(y-b);
}

int main(){
    freopen("1.in","r",stdin);
    freopen("strange.out","w",stdout);
//  scanf("%s",s);//不知道爲什麼,在測評機上的時候用scanf就不會錯,用cin會錯
//  l=strlen(s);
    cin>>s;
    l=s.size();
    cout<<l<<endl;
    num[0]=1;
    vis[s[0]]=1,cnt++;
    for(int i=1;i<l;i++){
        if(s[i]==s[i-1])num[i]=num[i-1]+1;
        else cc.push_back(s[i-1]),vv.push_back(num[i-1]),num[i]=1;
        if(vis[s[i]]==0){
            vis[s[i]]=1;
            cnt++;
        }
        else{
            if(num[i]>vis[s[i]]){
                cnt+=num[i]-vis[s[i]];
                vis[s[i]]=num[i];
            }
        }
    }
    cc.push_back(s[l-1]),vv.push_back(num[l-1]);
    l=cc.size();
    for(int i=0;i<l-1;i++) mp[cc[i]-'a'][cc[i+1]-'a'].push_back((node){vv[i],vv[i+1]});
    for(int i=0;i<26;i++){
        for(int j=0;j<26;j++){
            l=mp[i][j].size();
            if(l==0) continue;
            sort(mp[i][j].begin(),mp[i][j].end());
            int maxx=mp[i][j][0].pre,maxy=mp[i][j][0].nex;
            ans+=maxx*maxy;
            for(int k=1;k<l;k++){
                if(mp[i][j][k].nex>maxy){
                    ans+=check(maxy,mp[i][j][k].pre,mp[i][j][k].nex);
                    maxy=mp[i][j][k].nex;
                }
            }
        }
    }
    printf("%lld",ans+cnt);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章