原题链接:
=====================================================================
参考题解:
蓝桥杯2023年第十四届省赛真题-子串简写(超时改写)-Dotcpp编程社区
====================================================================
说明:两层循环的话会超时,缺点在于没有充分利用上一个找到的c2符合条件的子串数量。
#include<bits/stdc++.h>
#define int long long
const int N=1e6;
using namespace std;
int k;
char c1,c2;
string str;
signed main() {
cin>>k;
cin>>str;
cin>>c1>>c2;
int count=0;int nums=0;
int n=str.size();
for(int i=k-1;i<n;i++){
//从k-1个位置开始数是否距离有k长度的c1
if(str[i-k+1]==c1) nums++;
//如果这个位置上出现c2,那么符合条件的数量就加上nums,因为nums是统计的所有c1到c2
//的距离肯定是大于等于k的
if(str[i]==c2) count+=nums;
}
cout<<count;
return 0;
}
==============================================================
尝试用前缀和(相当于是求前i项有多少个c1,把一个位置看成数列的an项,他的值要么为0要么为1,因为他要么是c1要么不是):
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5*1e5+10;
string s;
int k;
char c1,c2;
//前缀和,前缀和为下面的numsc1
//numsc1表示i位置上,从0到i有多少个c1
int numsc1[N]={0};
signed main() {
cin>>k;
cin>>s;
cin>>c1>>c2;
int len=s.size();
int ans=0;
if(s[0]==c1) numsc1[0]=1;
for(int i=1;i<len;i++){
if(s[i]==c1) {
numsc1[i]=numsc1[i-1]+1;
}
else{
numsc1[i]=numsc1[i-1];
}
if(s[i]==c2&&i>=k-1){//注意位置要合法
//该处的c2距离大于k的c1看 numsc1[i-k+1],因为这个c2和这个位置相距离k,这个位置的前缀和是在他之前的所有c1数量,这些c1到目前的c2距离肯定是大于等于k的
ans+=numsc1[i-k+1];
}
}
cout<<ans;
return 0;
}
===========================================================
线性查找:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=45;
string s;
int k=0;
char c1,c2;
vector<int> pc1;
//想到二分法之前的线性查找
signed main() {
cin>>k;
cin>>s;
cin>>c1>>c2;
int len=s.size(),ans=0;
if(len<k) cout<<ans;
for(int i=0;i<len;i++){
if(s[i]==c1){
pc1.push_back(i);
}
if(s[i]==c2&&i>=k-1){
int n=pc1.size();
int l=0,r=n-1;
for(r;r>=0;){
if(i-pc1[r]+1<k)
{
r--;
}
else{
break;
}
}
//注意这个判断,可能找完整个pc1数组都可能 没有合法的,所以需要特殊判断
if(pc1[r]<=i+1-k){
ans+=(r+1);
}
}
}
cout<<ans;
return 0;
}
由线性查找想到用二分提高效率(因为满足递增,pc1保存的所有c1的索引,从最后一个元素到第一个元素,到当前搜索的字符串位置i的距离是递增的,所以可以二分):
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=45;
string s;
int k=0;
char c1,c2;
vector<int> pc1;
//二分法,将c1全部找出来,在全是c1的数组pc1中进行二分查找,保证了递增关系
signed main() {
cin>>k;
cin>>s;
cin>>c1>>c2;
int len=s.size(),ans=0;
if(len<k) cout<<ans;
for(int i=0;i<len;i++){
if(s[i]==c1){
pc1.push_back(i);
}
if(s[i]==c2&&i>=k-1){
int n=pc1.size();
int l=0,r=n-1;
while(l<r){
int mid=(l+r+1)>>1;
if(i-pc1[mid]+1>=k) l=mid;
else r=mid-1;
}
if(pc1[l]<=i+1-k){
ans+=(l+1);
}
}
}
cout<<ans;
return 0;
}
二分模板:https://blog.csdn.net/weixin_72060925/article/details/127835264
要注意计算mid时要r+l+ **1** 再除以2,因为二分时取的l=mid,c++是向下取整,当出现l=r-1的时候可能会陷入死循环,详情见上面的链接。