ISIJ 2018 奇怪的字符串

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

无忧公主 2018-07-10

题目名称:奇怪的字符串
文件名称:strange.in / strange.out
题目描述

考虑字符串 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值