8.8 牛客多校联赛第七场部分题解

C题

题意

见代码

思路

见代码

代码

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
int T, n;
int flag[N], a[N], b[N], c[N], p[N];

// 题意: 给定n 以及一个数组A 有n个元素 每个元素都是1-n中的任意一个数(可重复)
// 问 能不能找到一个n个元素的排列P 元素由 1-n 构成 不能有重复  并且所有Pi != Ai

// 思路:先记录1-n所有出现过的数字 以及他们首次出现的位置 其它位置表示没有使用过
// 然后将这些数 以及他们对应的首次出现的位置 循环移动一位 就可以保证在这些位置Pi != Ai
// 然后再将没有出现过的数  随意分配在没有使用的位置上即可
int main()
{
	scanf("%d",&T);
	while(T--){
        scanf("%d",&n);
        memset(flag, 0, sizeof(flag));
        
		vector<int>v;
        int f = 0, first = 0;
        for(int i = 1; i <= n ; i++) {
            p[i] = 0;
            scanf("%d", &a[i]);
            
            if(i >= 2 && a[i] != a[i-1]) f = 1;
            
            if(flag[a[i]] == 0){ // 只处理首次出现的位置
                flag[a[i]] = 1;  // 标记a[i] 出现过
                
                if(first == 0) first = i; // 第一个出现的数的位置
              
                if(v.size()) p[i] = v.back(); // 所有出现过的数的位置循环移动一位
                v.push_back(a[i]);
            }
        }
        p[first] = v.back(); //第一个出现的数的位置上的数 移动后就是最后一个数
        
        if(f == 0) { // 如果序列全是同样的数 输出NO
            puts("NO");
            continue;
        }
        
        int kb = 0, kc = 0;
        for(int i = 1; i <= n; i++){
            if(p[i] == 0) b[kb++] = i; // 记录没有用到的位置i
            if(flag[i] == 0) c[kc++] = i;  // 记录没有出现的数i
        }
        // kb和kc一定相等      
       for(int i = 0; i < kb; i++) p[b[i]] = c[i]; // 为没有出现的数c[i] 分配在没有使用的位置上b[i]
        
        puts("YES");
        for(int i = 1; i<=n; i++) printf("%d ", p[i]);
        puts("");
	}
	return 0;
}

F题

题意

见代码

思路

见代码

代码

#include<bits/stdc++.h>
using namespace std;

// 题意: 给定循环数组n 和一个特定的数x 可以进行操作如下:
// 相邻两个数(第1和第n也是相邻)  如果相同或者相加等于x  就可以直接删掉这两个数
// 问最多可以进行多少次操作

// 思路: 可以发现删除策略并不会影响结果 就是说只要发现可以删除的一对 直接删掉就行了
// 不需要考虑某个数是和前面一个数的匹配删除还是和后面一个数匹配删除 得到的最终结果最优
// 比如 ...A B C...  假如B可以和两边的A或C匹配  那么其实他们三个就是等价的 ABC可以互相匹配
// 所以不管和左边还是和右边匹配 最后的结果就是三个数剩下一个数  所以剩下的是谁都一样

// 所以用双端队列来实现 输入a的时候和队尾比较看看能不能删 不能则放进队尾 否则把队尾删掉
// 最后再处理一下一头一尾的情况 如果能删 就删一头一尾
int n, x;
deque<int>dq;

int main()
{
    cin>>n>>x;
    int res = 0;
    for(int i =0; i<n; i++) {
        int a;
        cin>>a;
        if(dq.size() && (dq.back() == a || dq.back() + a == x)){ //如果能够和队尾匹配
            dq.pop_back(); //删掉队尾
            res++; //结果数+1
        }
        else dq.push_back(a); //否则 入队
    }
    // 最后还要处理一下一头一尾的情况
    while(dq.size()>=2 && (dq.front() + dq.back() == x || dq.front() == dq.back()) ){
        res++;
        dq.pop_front();
        dq.pop_back();
    }
    
    cout<<res<<endl;
    return 0;
}

正则表达式

题意

给定一个字符串,问正则表达式来匹配的话,匹配到该结果的表达式最短长度是多少,以及这个长度的匹配表达式种类有多少?

思路

见代码

代码

#include<bits/stdc++.h>
using namespace std;

// 正则表达式匹配规则

// (1) 字符串 s 则匹配字符串s
// (2) .  匹配一个任意字符 注意是一个字符! ab. 匹配以ab开头的长度为3的字符串   .ab匹配以ab结尾 长度为3的字符串
// (3) *  复制前面字符任意次  比如  a*  可以匹配a  aa aaa  aaaa 即全a字符串  a*b 匹配ab  aab  aaaab 等等
// (4) +  复制前面任意字符>=1次 比如 a+ 可以匹配 aa aaa  aaaa等等 但不可以匹配a  a+b 匹配aab  aaab  aaaaaaab等等
// (5) .+ 表示长度至少大于1的字符串  +表示复制 . 至少一次 比如a.*b表示匹配以a开头 b结尾 长度至少为3的字符串
// (6) .* 匹配长度大于等于0的字符串  *表示复制 . 任意次 比如a.*b匹配 任意以a开头 b结尾的字符串
// (7) ? 表示前一个字符可有可恶无 比如 abc?d 匹配的是 abd 或者abcd 即c可有可无
// (8) .*? 表示尽可能长度短  比如 a.*?b 匹配的是a开头b结尾 长度最短的字符串 比如有 ab acb acdb 则匹配的是ab 【.+?则是acb】

int main()
{
    int t;
    string s;
    cin>>t;
    while(t--){
        cin>>s;
        if(s.size() == 1)  cout<<1<<" "<<2<<endl;  // a 或 .  两种
        
        else{  // 长度>=2  可能有8种 : ab   a.    .a    a*    a+   .+    .*   ..  
            int f = 1; // 字符串中字符是否全相同
            for(int i = 1; i<s.size(); i++)
                if(s[i] != s[i-1]) f = 0;
            
            if(f && s.size() == 2) cout<<2<<" "<<8<<endl; // 全部
            if(f && s.size() > 2) cout<<2<<" "<<4<<endl; // 全相同 长度大于2  a*   a+   .+   .*
            if(!f && s.size() == 2) cout<<2<<" "<<6<<endl; // ab  a.  .a  .+   .*  ..
            if(!f && s.size() > 2) cout<<2<<" "<<2<<endl; // .+  .*
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

GTTwelve

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

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

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

打赏作者

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

抵扣说明:

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

余额充值