【NOIP模拟】奇怪的字符串

题面

考虑字符串 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)中有多少个字符串是奇怪的?

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

分析

显然奇怪的只有两种,要么是aabb型要么是aaaa型,即不能由超过三部分组成。

对于aaaa型很简单,只需要统计连续的相同字母的数量的最长是多少。

对于aabb型,会有很多重复的情况,怎么计算呢?

考虑这两个 aaabb和aabbb,如果按2*3和3*2分别算,显然中间有重复计算,算了aaabb的2*3后,第二个具有的贡献仅有abbb和aabbb,即(3-2)*2

这是不是很像一个区间覆盖问题?我们把一个aabb型的写成(l,r)的点对形式,上面这两个分别是(3,2)和(2,3)

我们对于相同的两个字母(上面是a,b),把他们按l从小到大排序,如果r超过了之前最大的r,则会产生l*(r-maxr ) 的贡献,就像上面的2*(3-2)一样

对于aab这种,显然已经被完全覆盖了,又因为它的r并没有超过maxr,所以它也自然不会被计算。

代码

#include<bits/stdc++.h>
using namespace std;
#define N 200020
int n,ans;
int len[30];
char s[N];
struct email{int l,r;};
vector<email>v[30][30];
bool cmp(email a,email b){return a.l>b.l;}
void pre()
{
    int i=1,j,l=1,r;char ml=s[1],mr;
    while(s[i]==s[i+1])i++,l++;
    while(i<n)
    {
        j=i+1;mr=s[j];r=1;
        while(j<n&&s[j]==s[j+1])j++,r++;
        v[ml-'a'][mr-'a'].push_back((email){l,r});
        i=j,l=r,ml=mr;
    }
}

inline void cal(int x,int y)
{
    sort(v[x][y].begin(),v[x][y].end(),cmp);
    int i=0;email p=v[x][y][i];
    int ml=p.l,mr=p.r;ans+=ml*mr;i++;
    for(;i<v[x][y].size();i++)
    {
        email p=v[x][y][i];
        if(mr<p.r)ans+=p.l*(p.r-mr),mr=p.r;
    }

}

int main()
{
    scanf("%s",s+1);n=strlen(s+1);
    for(int i=1;i<=n;)
    {
        int j=i;
        while(j<n&&s[j]==s[j+1])j++;
        len[s[i]-'a']=max(len[s[i]-'a'],j-i+1);
        i=j+1;
    }
    for(int i=0;i<26;i++)ans+=len[i];
    pre();
    for(int i=0;i<26;i++)
        for(int j=0;j<26;j++)
            if(v[i][j].size())
                cal(i,j);
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/NSD-email0820/p/9891309.html