Code Jam--The Repeater

本文探讨了一道Google Code Jam比赛中的问题,涉及到字符串操作和优化策略。通过理解两种特定字符串操作的影响,作者展示了如何通过操作使得一组字符串达到一致,并计算了所需的最小操作数。该文提供了一个算法解决方案,包括字符串简化、频率分析和最优代价计算等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题:https://code.google.com/codejam/contest/2994486/dashboard

题意:Fegla给了Omar N组长度不超过一百且全部由小写字母组成的字符串,Omar可以对每个字符串进行两种操作,第一种操作就是添加一个相同的字符在某个字符旁边,第二种操作就是删除连续出现的字符中的一个。问通过这两种操作,Omar是否能使这N组字符串完全一样,若能则输出最小的操作数。

题解:关键就是要理解两种操作带来的影响,首先定义一个字符串的简单字符串:将原字符串中连续出现的字符只用一个字符表示。例如:aaabbbcc的简单字符串就是abc

  • 易知执行两种操作后的字符串只会改变字符出现的频率,它的简单字符串是不会变化的,也即最终的字符串和所有N组字符串的简单字符串都是一样的。
  • 假设N组中简单字符串的第i个字符出现的频率最小最大分别为mink,maxk,易知这N组中的第i个简单字符的最优代价频率不会超出[mink,maxk]的范围。
注意:要将字符串全部输入。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define  _abs(x) (((x) > 0) ? (x) : (-1*(x)))
#define  INF 0X7F7F7F7F

class solve
{
private:
    int T;
    int N;
    char unit[105];
    int unitLen;
    vector<vector<int> > num;
public:
    solve(int t):T(t)
    {
        scanf("%d",&N);
        num.resize(N+1);
        memset(unit,0,sizeof(unit));
        printf("Case #%d: ",T);
        if(processIn())
        {
            printf("Fegla Won\n");
        }
        else
        {
            printf("%d\n",calcMinStep());
        }
    }
    int processIn();
    int calcMinStep();
    int checkM(int row,char flag);
    int calcStep(int row,int sameNum);
};

int solve::calcStep(int row,int sameNum)    //计算所有字符串中第row个简单字符变成频率为sameNum的代价
{
    int re = 0;
    for(int i = 0;i < N;i++)
    {
        re += _abs(num[i][row]-sameNum);
    }
    return re;
}

int solve::checkM(int row,char flag)
{
    int re = INF;
    if(flag)       //最大值
    {
        re = 0;
    }
    for(int i = 0;i < N;i++)
    {
        if(flag)
        {
            re = max(num[i][row],re);
        }
        else
        {
            re = min(num[i][row],re);
        }
    }
    return re;
}

int solve::calcMinStep()
{
    int minStep = 0;
    int unitMinStep;
    int mink,maxk;
    unitLen = strlen(unit);
    for(int i = 0;i < unitLen;i++)
    {
        mink = checkM(i,0);
        maxk = checkM(i,1);
        unitMinStep = INF;
        for(int k = mink;k <= maxk;k++)     //最终相同字符串中unit[i]的频率只可能是mink到maxk中间的值
        {
            unitMinStep = min(unitMinStep,calcStep(i,k));
        }
        minStep += unitMinStep;
    }
    return minStep;
}

int solve::processIn()
{
    char tmpStr[105];
    char pre;
    char IsWon = false;
    int len;
    int unitIndex;
    int tmpNum;
    getchar();
    for(int i = 0;i < N;i++)
    {
        memset(tmpStr,0,sizeof(tmpStr));
        gets(tmpStr);
        if(IsWon)       //Fegla已经赢了则不需要再处理字符串,但是字符串必须输入完毕
            continue;
        pre = 0;
        unitIndex = 0;
        tmpNum = 0;
        len = strlen(tmpStr);
        for(int j = 0;j < len;j++)
        {
            if(tmpStr[j] != pre)    //第一次出现不同于前面邻接字符
            {
                pre = tmpStr[j];
                if(tmpNum != 0)
                {
                    num[i].push_back(tmpNum);
                }
                if(i == 0)      //记录第一组的简单字符串
                {
                    unit[unitIndex] = pre;
                }
                else
                {
                    if(unit[unitIndex] != pre)      //后面的简单字符串不能和第一组的对应则Fegla赢
                    {
                        IsWon = true;
                    }
                }
                ++unitIndex;
                tmpNum = 1;
            }
            else
            {
                tmpNum++;
            }
        }
        num[i].push_back(tmpNum);       //输入最后一个字符的频率
        if(i != 0&&num[i].size() != num[i-1].size())    //后面的简单字符串长度不同于第一组则Fegla赢
        {
            IsWon = true;
        }
    }
    return IsWon;
}

int main()
{
    int t;
    scanf("%d",&t);
    for(int i = 1;i <= t;i++)
    {
        solve repeater(i);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值