2021CCPC网络赛B题Time-division Multiplexing(HDU7101)

一、题目描述

链接http://acm.hdu.edu.cn/showproblem.php?pid=7101
Problem Description
Time-division multiplexing (TDM) is a method of transmitting and receiving independent signals over a common signal path by means of synchronized switches at each end of the transmission line so that each signal appears on the line only a fraction of time in an alternating pattern. This method transmits two or more digital signals or analog signals over a common channel.

The time domain is divided into several recurrent time slots of fixed length, one for each sub-channel. A sample byte or data block of sub-channel 1 is transmitted during time slot 1, sub-channel 2 during time slot 2, etc. One TDM frame consists of one time slot per sub-channel plus a synchronization channel and sometimes error correction channel before the synchronization. After the last sub-channel, error correction, and synchronization, the cycle starts all over again with a new frame, starting with the second sample, byte or data block from sub-channel 1, etc.

There are n time slots in total now, each of which periodically transmits a string only containing lowercase letters. You need to find the shortest slot length that contains all the different letters transmitted totally.
输入输出和样例
题意解释
共T组数据,每组数据有N个字符串。这N个字符串会生成一个总的字符串,生成规则是首先每个字符串各取其第一个字符加入总字符串,然后再分别取其第二个字符加入总字符串……当取到某个字符串的最后一个字符后,下一次就取该字符串的第一个字符。这样一直循环取下去,生成一个总字符串。
要求的是,在总字符串中选取一个子串,该子串包含N个字符串中的所有字母,而且长度要最短,问我们最短长度是多少。

二、题解

  1. 总串长度,由于每个字符串的长度不超过12,所以出现的长度可能为1 ~ 12.
    lcm(1, 2, … 12)为27720,所以总串的一个循环长度就是27720 * N。
    如果我们要使总串当中包含所有出现的可能的话,需要再接上一个循环,也就是27720 * N * 2。这个长度不超过6e6
  2. 现在题目就是在一个长度为6e6的字符串中,找到一个最短的字串包含字符串中出现的所有字母。
  3. 可以二分最短长度,依次验证;也可以用双指针。

三、AC代码

  1. 二分
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;
const int N = 6e6 + 10;
bool vis[30];
char s[N], st[110][20];
int num[30], len[110], pos[110], n, kindnum, length;

bool check()
{
    for (int i = 1; i <= n; i++)
    {
        if (pos[i] != 0)
            return false;
    }
    return true;
}

bool check1(int mid)
{
    memset(num, 0, sizeof num);
    int kind = 0;
    for (int i = 0; i < mid; i++)
    {
        if (num[s[i] - 'a'] == 0)
        {
            kind++;
        }
        num[s[i] - 'a']++;
    }

    if (kind == kindnum)
    {
        return true;
    }

    int l = 0;
    for (int r = mid; r < 2 * length;l++, r++)
    {
        if (--num[s[l] - 'a'] == 0)
            kind--;
        if (++num[s[r] - 'a'] == 1)
            kind++;
        if (kind == kindnum)
            return true;
    }
    return false;
}

int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        memset(pos, 0, sizeof pos);
        memset(vis, 0, sizeof vis);
        cnt = kindnum = 0;
        bool f = false;
        scanf("%d", &n);
        for (int i = 1; i <= n;i++)
        {
            scanf(" %s", st[i]);
            len[i] = strlen(st[i]);
        }

        for (int i = 1;i <= n; i++)
        {
            for (int j = 0; st[i][j]; j++)
            {
                if (!vis[st[i][j] - 'a'])
                {
                    kindnum++;
                    vis[st[i][j] - 'a'] = true;
                }
            }
        }

        int i;
        for (i = 0; !f;)
        {
            for (int j = 1; j <= n; j++, i++)
            {
                s[i] = st[j][pos[j]];
                pos[j] = (pos[j] + 1) % len[j];
            }
            f = check();
        }
        length = i;

        for (int j = 0;j < length; j++)
        {
            s[i + j] = s[j];
        }
        s[length * 2] = 0;

        int l = kindnum, r = length;
        while (l < r)
        {
            int mid = l + r >> 1;
            if (check1(mid))
            {
                r = mid;
            }
            else
            {
                l = mid + 1;
            }
        }
        
        printf("%d\n", l);
    }
    system("pause");
    return 0;
}
  1. 双指针
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;
const int N = 6e6 + 10;
char s[N];
int num[30];
char st[110][20];
bool vis[30];

int len[110], pos[110], n, kindnum, length;

bool check()
{
    for (int i = 1; i <= n; i++)
    {
        if (pos[i] != 0)
            return false;
    }
    return true;
}

int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        memset(pos, 0, sizeof pos);
        memset(vis, 0, sizeof vis);
        memset(num, 0, sizeof num);
        kindnum = 0;
        bool f = false;
        scanf("%d", &n);
        for (int i = 1; i <= n;i++)
        {
            scanf(" %s", st[i]);
            len[i] = strlen(st[i]);
        }

        for (int i = 1;i <= n; i++)
        {
            // *共有kindnum种字符
            for (int j = 0; st[i][j]; j++)
            {
                if (!vis[st[i][j] - 'a'])
                {
                    kindnum++;
                    vis[st[i][j] - 'a'] = true;
                }
            }
        }

        int i;
        for (i = 0; !f;)
        {
            // *信道中的字符
            for (int j = 1; j <= n; j++, i++)
            {
                s[i] = st[j][pos[j]];
                pos[j] = (pos[j] + 1) % len[j];
            }
            f = check();
        }
        length = i;

        for (int j = 0;j < length; j++)
        {
            s[i + j] = s[j];
        }
        length *= 2;
        s[length] = 0;

        int ans = 100, l = 0, r = -1;

        while (r < length - 1)
        {
            if (++num[s[++r] - 'a'] == 1) kindnum--;
            // *左指针右移到所指元素与右指针所指元素不同
            while (l < r && s[l] == s[r]) --num[s[l++] - 'a'];

            if (kindnum == 0)
            {
                ans = min(ans, r - l + 1);
                // *右移左指针使某个字母消失为止
                while (l < r && --num[s[l++] - 'a']);
                kindnum++;
            }
            // *右移左指针至如果再右移会使某个字母消失为止
            while (l < r && num[s[l] - 'a'] > 1) --num[s[l++] - 'a'];
        }
        
        printf("%d\n", ans);
    }
    system("pause");
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值