OneCode(天梯三) D:涯涯的字符串(题解)

D:

本题正规解法为 深搜+状态压缩 

当然,本人并没有使用这种方法,本人选择的是 贪心策略

程序如下:

#pragma GCC optimize(2)  //O2优化(比赛禁用)
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<algorithm>

using namespace std;
int n;
int mapn[30][27];
int mapall[27];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
         char ch[100001];
         cin>>ch;
         int l=strlen(ch);
         for(int j=1;j<=l;j++)
         {
             if(mapn[i][ch[j-1]-'a'+1]+1>=2) mapn[i][ch[j-1]-'a'+1]=0;
             else mapn[i][ch[j-1]-'a'+1]++;
             mapall[ch[j-1]-'a'+1]++;
         }
    }
    int g=0;
    for(int i=1;i<=26;i++) 
    {
        if(mapall[i]%2==1) g++;
    }
    if(g==0)
    {
        cout<<n<<endl;
        for(int k=1;k<=n;k++)
        {
            cout<<k<<' ';
        }
        return 0;
    }
    int maxn=-1,num=0,v=0,w=0;
    int t;
    for(t=n;t>=1;t--)
    {
        maxn=-1;
        for(int i=1;i<=n;i++)
        {
            if(mapn[i][0]==0)
            {
                v=0,w=0;
                for(int j=1;j<=26;j++)
                {
                    if((mapall[j]-mapn[i][j])%2==0) v++;
                    else w++;
                }
                if(maxn<v)
                {
                    maxn=v;
                    num=i;
                }
                if(w==0)
                {
                    cout<<t-1<<endl;
                    mapn[i][0]=1;
                    for(int k=1;k<=n;k++)
                    {
                        if(!mapn[k][0]) cout<<k<<' ';
                    }
                    return 0;
                }
            }
        }
        mapn[num][0]=1;
        for(int i=1;i<=26;i++)
        {
            mapall[i]-=mapn[num][i];
        }
    }
 } 

我写这个程序的时候,根本都不知道我在写的是什么

写法解读:

读入字符串,储存到 mapn[i][j]

当然并不是直接存放,而是记录下各个字母所出现的个数。

for(int i=1;i<=n;i++)
{
         char ch[100001];
         cin>>ch;
         int l=strlen(ch);
         for(int j=1;j<=l;j++)
         {
             if(mapn[i][ch[j-1]-'a'+1]+1>=2) mapn[i][ch[j-1]-'a'+1]=0;
             else mapn[i][ch[j-1]-'a'+1]++;
//mapn[i][j]储存第i个字符串中第j个字母出现的次数(字典顺序)
             mapall[ch[j-1]-'a'+1]++;  //储存全部字符串中各个字母出现次数
         }
}

但直接++也是太暴力了,本人选择加上小处理,如上红色代码段

因为本题要使同个字母出现个数成偶数,所以本人把在同个字符串里凑成对的字母的出现次数直接归为 0

让mapn[i][j]里只有0和1,方便处理

 

特殊处理,如果全部字母的出现次数都为偶数,直接输出就行了

  for(int i=1;i<=26;i++) 
    {
        if(mapall[i]%2==1) g++;
    }
    if(g==0)
    {
        cout<<n<<endl;
        for(int k=1;k<=n;k++)
        {
            cout<<k<<' ';
        }
        return 0;
    }

 

三层循环,数据范围小,无所畏惧

int maxn=-1,num=0,v=0,w=0;
int t;
for(t=n;t>=1;t--) //循环1
{
        maxn=-1;
        for(int i=1;i<=n;i++) //循环2
        {
            if(mapn[i][0]==0)
            {
                v=0,w=0;
                for(int j=1;j<=26;j++) //循环3
                {
                    if((mapall[j]-mapn[i][j])%2==0) v++; //记录奇或偶数的出现次数
                    else w++;
                }
                if(maxn<v) //选择更接近(出现次数)全部偶数的字符串
                {
                    maxn=v;
                    num=i;
                }
                if(w==0) //当没有奇数情况出现
                {
                    cout<<t-1<<endl;
                    mapn[i][0]=1;
                    for(int k=1;k<=n;k++)
                    {
                        if(!mapn[k][0]) cout<<k<<' ';
                    }
                    return 0;
                }
            }
        }
        mapn[num][0]=1; //更改标签
        for(int i=1;i<=26;i++) //更新mapall的值
        {
            mapall[i]-=mapn[num][i];
        }
}

揭露 map[i][0] 的功能:记录字符串是否被使用,0为“否”,1为“是”

循环(1):for(t=n;t>=1;t--)

意义:保留 t -1个字符串

循环(2):for(int i=1;i<=n;i++)

意义:枚举到第i个字符串

循环(3):for(int j=1;j<=26;j++)

意义:26个字母中第j个字母(字典顺序)

综上,很容易发现,本人的思路是:

         记录字母出现的总次数,用 所有字符串字母出现次数的总数 去减 一个字符串中字母出现的次数,得出的差再与减去其他字符串得出的差比较,看哪个出现次数为偶数的情况多就删除哪个。(此处比较难理解,用心总会明白的)

补充:变量v、w,分别用于记录剩下字符串中出现次数为偶数、奇数的字母的个数

 

[ The End. ]

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值