Gym 101612C Consonant Fencity 建图预处理 + 二进制枚举

本文介绍了一种解决特定字符串问题的高效算法。通过构建辅音字母图,并使用二进制枚举法来最大化辅音配对数,该算法能够在合理时间内解决长度达1e6的字符串问题。

题目https://cn.vjudge.net/problem/Gym-101612C

题意:给出一个字符串(长度不超过1e6),仅由小写字母组成。定义一个叫consonant fencity的东西,含义是如果两个连续字母是辅音(本题中辅音指除aeiouwy以外的字母),且大小写不相同,就有一对consonant fencity。题目要求指定每个字母的大小写形式(要求相同字母的大小写形式相同),输出consonant fencity最多的字符串。

思路:显然可以枚举每种字母的大小写形式的组合来求解,但直接枚举再计算的复杂度高达O(n * 2^19),这显然没法接受。考虑到辅音字母是固定的,可以每种辅音字母看作一个结点,将字符串中存在两个字母构成consonant fencity看作有一条从在前的字母到在后的字母的有向边,将这两个字母构成consonant fencity的次数看作边权建图,再用二进制枚举计算时直接扫图即可求解,这样复杂度就降到了O(n)的预处理+19*19*2^19大概1.9e8,对于3秒的时间可以接受。

代码:C++
这里我用bitset进行二进制枚举,当然也可以用位运算

#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
#include <algorithm>
#include <cctype>
#include <bitset>
#include <cmath>
using namespace std;

const int maxn = 1000000 + 10;
const int maxm = 19;
const char consonants[20] = "bcdfghjklmnpqrstvxz";
int toint[200];

char s[maxn];
int len;

int pic[maxm][maxm];

int maxx = 0;
int maxstatus = 0;

bool isconsonant(char c)
{
    return !(c == 'a' || c == 'e' || c == 'i'|| c == 'o' || c == 'u' || c == 'w' || c == 'y');
}

void init()
{
    for(int i = 0; i < 19; i++)
    {
        toint[consonants[i]] = i;
    }
}

void buildpic()
{
    for(int i = 0; i < len - 1; i++)
    {
        if(isconsonant(s[i]) && isconsonant(s[i+1]))
        {
            pic[toint[s[i]]][toint[s[i+1]]]++;
        }
    }
}

int solve(bitset<32> st)
{
    int ans = 0;
    for(int i = 0; i < maxm; i++)
    {
        for(int j = 0; j < maxm; j++)
        {
            if(pic[i][j] && st[i] ^ st[j])
            {
                ans += pic[i][j];
            }
        }
    }
    return ans;
}

int main()
{
    freopen("consonant.in", "r", stdin);
    freopen("consonant.out", "w", stdout);
    scanf("%s", s);
    len = strlen(s);
    init();
    buildpic();
    int statusnum = 1 << 19;
    for(int i = 0; i < statusnum; i++)
    {
        int t = solve(bitset<32>((unsigned int)i));
        if(t > maxx)
        {
            maxx = t;
            maxstatus = i;
        }
    }
    bitset<32> ans((unsigned)maxstatus);
    for(int i = 0; i < len; i++)
    {
        if(ans[toint[s[i]]])
        {
            putchar(toupper(s[i]));
        }
        else
        {
            putchar(s[i]);
        }
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值