一、题目描述
链接: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个字符串中的所有字母,而且长度要最短,问我们最短长度是多少。
二、题解
- 总串长度,由于每个字符串的长度不超过12,所以出现的长度可能为1 ~ 12.
lcm(1, 2, … 12)为27720,所以总串的一个循环长度就是27720 * N。
如果我们要使总串当中包含所有出现的可能的话,需要再接上一个循环,也就是27720 * N * 2。这个长度不超过6e6 - 现在题目就是在一个长度为6e6的字符串中,找到一个最短的字串包含字符串中出现的所有字母。
- 可以二分最短长度,依次验证;也可以用双指针。
三、AC代码
- 二分
#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;
}
- 双指针
#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;
}