[Wannafly挑战赛28][B msc和mcc][预处理+枚举]
链接:https://ac.nowcoder.com/acm/contest/217/B
来源:牛客网
msc和mcc
题目描述
msc和mcc是一对好朋友,有一天他们得到了一个长度为n的字符串s.
这个字符串s十分妙,其中只有’m’,’s’和’c’三种字符。
定义s[i,j]表示s中从第i个到第j个字符按顺序拼接起来得到的字符串。
定义一个字符串t的子序列为从t中选出一些位置并且将这些位置上面的字符按顺序拼接起来得到的字符串。
两个子序列重合当且仅当存在一个位置x使得两个子序列同时选择了位置x。
由于msc和mcc是一对很好很好的好朋友,所以她们希望选择两个数字x和y满足1≤x≤y≤n使得s[x,y]中同时存在两个**不重合的子序列**使得其中一个是’msc’且另外一个是’mcc’
现在给出n和字符串s,问她们可以选出多少对不同的(x,y)。
输入描述:
第一行一个正整数n,表示字符串s的长度。
第二行一个长度为n的字符串s,其中s只包含字符’m’,’s’和,’c’。(n<100000)
输出描述:
一行一个正整数,表示答案。
题目大意:给一个字符串,求有多少个区间内部存在msc 和 mcc 两个子序列(并且二者不存在公共元素)
题目分析:由于n是1e5级别的,并且是要对区间进行计数,所以只能在O(N)的时间复杂度下得到最终结果,可以想到枚举区间左端点O(N),然后就需要O(1)得到右端点的个数,右端点的个数也就是n-符合条件的最小右端点,所以这个时候只能预处理,而可以知道满足条件的长度为6的子序列只有8种,所以可以预处理出每种情况下的每一个元素后一个元素的位置,然后就能O(1)得到最小右端点了.
1 #include<iostream> 2 #include<cstring> 3 #include<cmath> 4 #include<map> 5 #include<cstdio> 6 #include<algorithm> 7 using namespace std; 8 typedef long long ll; 9 int n; 10 char ch[8][7]={ 11 "012022", 12 "010222", 13 "001222", 14 "002212", 15 "022012", 16 "020122", 17 "020212", 18 "002122" 19 }; 20 int nx[100005][4],kt[100005]; 21 char s[100005]; 22 int main(){ 23 cin>>n; 24 scanf("%s",s+1); 25 for(int i=1;i<=n;i++){ 26 if(s[i]=='m')kt[i]=0; 27 else if(s[i]=='s')kt[i]=1; 28 else kt[i]=2; 29 } 30 for(int i=0;i<3;i++){ 31 int pos=n+1; 32 for(int j=n;j>=0;j--){ 33 nx[j][i]=pos; 34 if(kt[j]==i)pos=j; 35 } 36 } 37 ll ans=0; 38 for(int i=1;i<=n;i++){ 39 int ed=n+1; 40 for(int j=0;j<8;j++){ 41 int pos=i-1; 42 for(int k=0;k<6&&pos<=n;k++){pos=nx[pos][ch[j][k]-'0'];} 43 ed=min(ed,pos); 44 } 45 ans+=n-ed+1; 46 } 47 cout << ans << endl; 48 return 0; 49 }