1. 程式人生 > >[Wannafly挑戰賽28][B msc和mcc][預處理+列舉]

[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 }
View Code