【题解】 小狗
题目描述
【题目描述】
小 Q 是个爱狗狂魔,他饲养了 N(N<=2000)条中华田园犬狗和 M(M<=2000)条秋田犬。
并且给每条狗取了一个英文名字,例如:Sally,Sussan,Lysa 等,
为了方便起见,小 Q 登记时只会登记首字母,例如 Sally、Sussan、Lysa 只会登记为 S、S、L。
现在中华田园犬和秋田犬分别从左至右各站成了一队。小 Q 准备带他们出去踏青了!
但因为踏青的路实在太窄,小 Q 不得不在踏青前把两条队伍合成一队。
那如何合呢?我们假设中华秋田犬所在的队伍为 A 队,秋田犬所在队伍为 B。
最后合成的新队伍为 C。大家知道的如果从队伍中间选择小狗加入新队伍,容易使原队伍混乱.
所以现在我们制定如下规则:
(1)每次从 A 队或者 B 队的头或者尾选取一条小狗加入到新的队伍 C 的尾部去(第一条选择的小狗为头)。
(2)重复(1)的操作,直到 A 队和 B 队的所有小狗都站到 C 队为止。
现在小 Q 想知道,按照这种规则,他登记的新队列的最小字典序为多大?
【输入格式】
第 1 行:两个整数 N 和 M.
第 2 行至第 N+1 行:每行一个大写字母,表示 A 队中小狗的头字母.
第 N+2 至 N+1+M 行:每行一个大写字母,表示 B 队中小狗的头字母.
【输出格式】
一行,得到的最小字典序的序列.
【样例输入】
3 3
A
C
D
B
C
B
【样例输出】
ABBCCD
【数据说明】
50%数据 1<=n,m<=100
100%数据 1<=n,m<=2000
题目大意
给定2个字符串(有且仅有大写字母组成),每次将任意一个字符串的头部或尾部加入新字符串,求新字符串最小字典序。
思路
设一个字符串为 \(s_1\),另一个字符串为 \(s_2\)。
由于要字典序最小,所以要在 \(s_1\) 的头部、尾部,\(s_2\) 的头部、尾部中找最小的字符加入。
但是,如果其中最小的字符有两个,又该选哪个呢?这时就应该往它们的下一个去看,但是这样一直往后找,就会太复杂。
所以可以给每个字符串建一个反字符串进行比较。例如:原字符串 \(s_1\) 为 \(CBAC\),那么就可以建一个反字符串 \(rs_1\) 为 \(CABC\),因为 \(s_1 < rs_1\),所以选尾部的 \(c\) 显然更优。
但是如果这2个字符串长度不同,就会发生错误。举个例子,字符串 \(s_1\) 为 \(AB\),\(s_2\) 为 \(ABB\)。如果此时按字典序进行比较,则有 \(s1 < s2\),但是先选择 \(s_1\) 显然更优。这时可以比较 $s_1 + s_2 $ 和 \(s_2 + s_1\),就可以实现。
代码
#include <bits/stdc++.h>
using namespace std;
int n, m;
char a[2005], b[2005];
string tmp[6];
int FindMin(int as, int ae, int bs, int be)
{
tmp[1] = tmp[2] = tmp[3] = tmp[4] = "";
// tmp[1] 是 字符串a,tmp[2] 是 字符串a的翻转
// tmp[3] 是 字符串b,tmp[4] 是 字符串b的翻转
for (int i = as, j = ae; i <= ae; i ++ , j -- )
tmp[1] += a[i], tmp[2] += a[j];
for (int i = bs, j = be; i <= be; i ++ , j -- )
tmp[3] += b[i], tmp[4] += b[j];
int t1 = 1, t2 = 3;
if (tmp[2] < tmp[1]) t1 = 2; // 相同长度直接比较
if (tmp[4] < tmp[3]) t2 = 4; // 相同长度直接比较
// 不同长度拼接比较
if (tmp[t2] == "" || tmp[t1] + tmp[t2] < tmp[t2] + tmp[t1]) return t1;
else return t2;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
for (int i = 1; i <= m; i ++ ) cin >> b[i];
int as = 1, ae = n, bs = 1, be = m; // as、ae 分别是 a 的起始和结束 , bs、be 分别是 b 的起始和结束
string ans = "";
for (int i = 1; i <= n + m; i ++ )
{
int ret = FindMin(as, ae, bs, be); // 找出选哪边
// 1 表示 选a的头部,2 表示 选a的尾部 ,3 表示 选b的头部,4 表示 选b的尾部
if (ret == 1) ans += a[as ++ ];
else if (ret == 2) ans += a[ae -- ];
else if (ret == 3) ans += b[bs ++ ];
else ans += b[be -- ];
}
cout << ans << '\n';
return 0;
}