Triplets
题意:给一串RGB组成的字串,计算满足两个条件的子序列数目:(1)子序列由三个互不相同的字母组成(2)任意两个字母之间的距离不能相等(一和二的距离不能等于二和三的距离)
思路:
本菜鸡因为缺乏经验所以一开始反应了一会儿…但感觉这种序列问题一般都是用区间(前缀和)来做。不过我一开始以为是要按照RGB这个顺序,就莫名磨了很久2333,加上很少写前缀和(菜)debug了很久(((躺尸
最外层六次循环,因为有六种排序。里面并列两个循环,第一个循环记录第三个字母从后往前的数目,同时记录第二个字母的位置;第二个循环从前往后扫描序列第一个字母,计算ans。
这样的作法时间复杂度在O(n)-O(n^2) 之间,我也不知道怎么算…只知道比常规方法的O(n^2)要快很多。跑AC用了22ms,常规作法用了63ms.
常规作法就是两层循环,分别扫描第一个字母和第二个字母。
其实本质差不多,不过记录第二个字母位置要快很多罢了。
代码:
#include<bits/stdc++.h>
using namespace std;
char s[4003];
int back[4003];
int tmp=0,last=0;
int pos[4003];
char zimu[]={'a','R','R','G','G','B','B'},zimu2[]={'a','G','B','R','B','R','G'},zimu3[]={'a','B','G','B','R','G','R'};
int main(){
int n;
scanf("%d",&n);
scanf("%s",s+1);
int cnt;
long long ans=0;
for(int k=1;k<=6;k++){
cnt=tmp=last=0;
memset(back,0,sizeof(back));
memset(pos,0,sizeof(pos));
for(int i=n;i>=1;i--){
if(s[i]==zimu3[k]){
tmp++;
}
if(s[i]==zimu2[k]){
pos[++cnt]=i;
back[i]=back[last]+tmp;
tmp=0;
last=i;
}
}
for(int i=1;i<=n;i++){
if(s[i]==zimu[k]){
for(int j=1;j<=cnt;j++){
if(pos[j]>i){
ans+=back[pos[j]];
if(pos[j]+(pos[j]-i)<=n&&s[pos[j]+(pos[j]-i)]==zimu3[k])ans--;
}
}
}
}
}
printf("%lld\n",ans);
}
常规作法:
#include<bits/stdc++.h>
using namespace std;
const int maxn=4005;
int nr[maxn],ng[maxn],nb[maxn];
char s[maxn];
int main(){
int n;
scanf("%d",&n);
scanf("%s",s+1);
for(int i=n;i>=1;i--){
nr[i]+=nr[i+1];
nb[i]+=nb[i+1];
ng[i]+=ng[i+1];
if(s[i]=='R')nr[i]++;
if(s[i]=='G')ng[i]++;
if(s[i]=='B')nb[i]++;
}
long long ans=0;
for(int i=1;i<=n-2;i++){
for(int j=i+1;j<=n-1;j++){
if(s[i]=='R'){
if(s[j]=='G')ans+=nb[j];
if(s[j]=='B')ans+=ng[j];
if(j-i+j<=n&&s[j-i+j]!=s[j]&&s[j]!=s[i]&&s[j-i+j]!=s[i])ans--;
}
if(s[i]=='G'){
if(s[j]=='R')ans+=nb[j];
if(s[j]=='B')ans+=nr[j];
if(s[j]!=s[i]&&j-i+j<=n&&s[j-i+j]!=s[j]&&s[j-i+j]!=s[i])ans--;
}
if(s[i]=='B'){
if(s[j]=='G')ans+=nr[j];
if(s[j]=='R')ans+=ng[j];
if(s[j]!=s[i]&&j-i+j<=n&&s[j-i+j]!=s[j]&&s[j-i+j]!=s[i])ans--;
}
}
}
printf("%lld\n",ans);
}