牛客挑战赛75:Substring Not Subsequence (子序列个数)

题目描述

给定长度为 nnn 的字符串 SSS,请求出有多少个非空字符串 TTT 满足:

  • TTT 作为子串在 SSS 中出现至少一次;
  • SSS 中不存在不连续的子序列等于 TTT。

输入描述:

 

第一行,一个正整数 nnn。

第二行,一个长为 nnn 的字符串 SSS。

输出描述:

一行,一个正整数,表示答案。

示例1

输入

复制5 abcaa

5
abcaa

输出

复制9

9

说明

 

共有 999 个 TTT 满足条件:a,ab,abc,abcaa,b,bc,bcaa,c,caa\texttt{a},\texttt{ab},\texttt{abc},\texttt{abcaa},\texttt{b},\texttt{bc},\texttt{bcaa},\texttt{c},\texttt{caa}a,ab,abc,abcaa,b,bc,bcaa,c,caa。

串 abca\texttt{abca}abca 不满足条件,因为取下标 1,2,3,51,2,3,51,2,3,5 可以形成 abca\texttt{abca}abca,但是这些下标不连续。

串 aba\texttt{aba}aba 也不满足条件,因为 aba\texttt{aba}aba 没有作为子串出现过。

备注:

  • 1≤n≤2×1051\le n\le 2\times 10^51≤n≤2×105;
  • SSS 中只含小写英文字母。

做法

我们拿样例abcaa找一下规律
ab
abc:bc
abcaa:bcaa,caa

在随便举一个例子:abcad
ab
abc:bc
abca:bca,ca
abcad:bcad,cad,ad
#include<bits/stdc++.h>
#define int long long
using namespace std;

int n;
string s;
int ans;
int id[30];//26个字母第一次出现的位置
int qzh[200010];//记录的当前字母是第一次出现的字母个数

signed main(){

    scanf("%lld",&n);
    cin>>s;

    unordered_map<char,int> mp;

    for(int i=0;i<26;i++) id[i]=-1;

    for(int i=0;i<s.size();i++) {
        mp[s[i]]++;
        if(id[s[i]-'a']==-1) id[s[i]-'a']=i;
    }

    if(n==1) {
        cout<<1<<endl;
        return 0;
    }

    if(n==2){
        if(mp.size()==1) cout<<2;
        else cout<<3;
        return 0;
    }
  
    for(int i=1;i<n;i++){

        string a;
        a+=s[i];

        qzh[i]=qzh[i-1];

        if(s.find(a,i+1)!=-1) {//当前位置的后面有这个字母,不满足条件2
            if(id[s[i]-'a']==i) qzh[i]=qzh[i-1]+1;
            continue;
        }  

        ans+=1;//s[0]到s[i]组成的子序列
        ans+=qzh[i];//起点是s[2]到s[i-1],终点是s[i]可以组成子序列个数(起点为第一次出现的字母就满足条件2)

        if(id[s[i]-'a']==i) qzh[i]=qzh[i-1]+1;

    }
    cout<<ans+mp.size();//记得加上子序列长度为1的
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值