题目描述
给定长度为 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的
}