Codeforces Round #710 (Div. 3)(A~D)(最大公共子串,离散化

题目链接

这个round做的很差。不知道是因为在学校不习惯还是因为自己又菜了。

比赛时做了3题,第4题一直wa。后来发现忘记判断奇偶性了,第二天起床试了一下就A了。所以勉强算我A了4题吧。

但这次前4题我都没有一发a的。A~D都在test 2上wa过……(太难过了)

接下来就是题解吧:

A.Strange Table

本题题意是对于n行m列的数字矩阵。按行展开时的   按列展开时数字k所在的位置   的值是多少。

因此我们先根据数字k。找到他是第几行第几列。

列的计算为(k-1)/n+1。

行的计算则需要特判几个情况。先计算k%n.如果这个值为0,则令他所在的行数为第n行,如果n=1.则令行数为1.否则行数就为k%n的这个值。

在根据求出的行列位置求出按行展开时该位置上的值即可。(一开始wa就是没想到n=1时的情况。并且把最大行数码成了m)

AC代码如下:

#include<bits/stdc++.h>
#define MAXN 100005
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
int t;
ll n,m,x;
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%lld%lld%lld",&n,&m,&x);
        ll c=(x-1)/n+1,r=x%n;//计算第几行和第几列 
        if(r==0)
            r=n;
        if(n==1)
            r=1;
//        printf("r:%d,c:%d\n",r,c);
        ll ans=(r-1)*m+c;
        printf("%lld\n",ans);
    }
    return 0;
}

B.Partial Replacement(贪心)

本题的题意是替换*为x。要求是对于第一个和最后一个*都要替换。然后串中任意两个x之间的距离不能大于k。求最少的替换次数。

本题的思路是贪心。

首先先找第一个*和最后一个*。由于题目保证一定有一个*。那么一定存在第一个*。

但由于可能只存在一个*。故不一定存在一个和第一个不同的*。所以如果找不到最后一个*,那么此时令cnt=1输出即可。

如果有多个*。那么将第一个*和最后一个*修改为x后。

从第一个*的位置开始贪心的搜索。从该位置+k的位置从后往前搜索第一个*。将其修改为x并将位置重定位为该位置再令cnt++即可。

需要注意的是如果第一个遍历到的不是*而是x说明最后一个*已经找到了,则直接返回即可。(一开始wa是循环中的continue和break退出和继续的循环没有写清楚……)

AC代码如下:

#include<bits/stdc++.h>
#define MAXN 100005
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
int t;
int n,k;
char s[55];
int main(){
    scanf("%d",&t);
    while(t--){
        memset(s,0,sizeof(s)); 
        scanf("%d%d",&n,&k);
        scanf("%s",s); 
        int flag=0,pre=0,cnt=0,pos=0;//flag为找第一个*,pre找上一个出现的* ,cnt为答案。 
        for(int i=0;i<n;++i){
            if(s[i]=='*'){
                if(!flag){
                    flag=1;
                    pos=i;
                    ++cnt;
                    s[i]='x';
                }else{
                    pre=i;//如果有第二个*,那么pre肯定不为0 
                }
            }
        }
        if(pre){//有末尾 
            ++cnt;//修改次数++ 
            s[pre]='x';//将最后一个*修改为x 
        }else{//无末尾,即只有一个* 
            printf("1\n");
            continue;;
        }
        while(pos+k<n){
            flag=0;
            for(int i=pos+k;i>pos;--i){
//                printf("i:%d\n",i);
                if(s[i]=='x'){//如果后面的数已经有x了,说明最后一个数在范围内 
                    flag=0;
                    break;
                }else if(s[i]=='*'){//如果第一个出现的是*。表明后面还有*或者x 
                    s[i]='x';//修改这个*为x 
                    ++cnt;//计数++ 
                    pos=i;//将倒数第二个x的地址改为这个点 
//                    printf("pos:%d\n",pos);
                    flag=1;
                    break;
                }
            }
            if(!flag)
                break;
        }
        printf("%d\n",cnt);
    }
    return 0;
}

C.Double-ended Strings.(最大公共子串)

题意是a,b两个字符串,可以分别删除串首和串尾的若干个字符。问最少删几个能令两个字符串相等。(如果两个字符串都为空则也相等)

本题就是求a,b两个字符串的最大公共子串。且我们注意到两个子串的长度都不大,只有20.(所以也可以直接暴力匹配

但是我突然发现我不会求两个字符串的最大公共子串orz……于是赶快上网抄了个模板。但一开始抄的模板有点问题……就wa了。

换了个模板抄就完事了。

现在我们来仔细看看如何求两个字符串的最大公共子串。(如果数据比较大好像要后缀自动机……)

AC代码如下:

#include <iostream>
#include <vector>
#include<algorithm>
#include<string>

using namespace std;
int lenSIZE(string a, string b) {
    //初始化a.size()*b.size()的二维数组,其数组元初始化为0
    vector<vector<int>> num(a.size());
    vector<int> temp(b.size(), 0);
    for (int i = 0; i < num.size(); i++) {
        num[i].resize(b.size());//开列数
        num[i]=temp;
    }

    int len = 0;
    for (int i = 0; i < a.size(); i++) {//
        for (int j = 0; j < b.size(); j++) {
            if (a[i] == b[j]) {
                if (i > 0 && j > 0) {
                    num[i][j] = num[i - 1][j - 1] + 1;
                    len=max(len, num[i][j]);
                }
                else {
                    num[i][j] = 1;
                    len = max(len, num[i][j]);
                }
                    
            }
        }
    }
    
    return len;
}
int main(int argc, char** argv) {
    int t;
    scanf("%d",&t);
    while(t--){
        string a,b;
        cin>>a>>b;
        int len = lenSIZE(a, b);
        cout<<(a.size()+b.size()-2*len)<<endl;
    }
    return 0;
}

 

D. Epic Transformation(离散化)

本题是给你一个数列。可以任意删去一对不相同的数。求最后最少可以剩几个。

我们可以通过玄学分析发现。当一个数的重复次数不超过n/2(n为数列长度)时,一定可以全部消掉。

同时由于a【i】的数据有点大。但一共也就2e5个数,因此我们可以对其进行离散化优化。

但这时还需要考虑n的奇偶性。当n为偶数时则全部消掉输出0,当n为奇数时则显然会剩下一个(因为是两个两个消的)。故输出1即可。

如果一个数的重复次数超过n/2.则n减去除该数以外的数的出现次数的数对(即*2)再输出即可。

AC代码如下:

#include <iostream>
#include <vector>
#include<algorithm>
#include<cstring>
#define MAXN 200005
using namespace std;
int a[MAXN],vis[MAXN];
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        memset(vis,0,sizeof(vis));
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
        }
        sort(a,a+n);
        int cnt=0;
        vis[0]=1;
        int maxn=0; 
        for(int i=1;i<n;i++){
            if(a[i]==a[i-1])
                vis[cnt]++;
            else{ 
                if(vis[cnt]>maxn)
                    maxn=vis[cnt]; 
                vis[++cnt]++;
            }
        }//0~cnt离散化。k
        if(vis[cnt]>maxn)
            maxn=vis[cnt];
        if(2*maxn>n)
            printf("%d\n",n-2*(n-maxn));
        else{
            if(n&1)
                printf("1\n");
            else
                printf("0\n");
        }
    }    
}

 

posted @ 2021-03-26 23:46  mikku  阅读(47)  评论(0)    收藏  举报