蓝桥杯2023年第十四届省赛真题-子串简写|前缀和|二分

原题链接:

蓝桥杯2023年第十四届省赛真题-子串简写 - C语言网

=====================================================================

参考题解:

蓝桥杯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的时候可能会陷入死循环,详情见上面的链接。

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

guts350

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值