CF508D Tanya and Password(欧拉路径)
文章目录
【注意】最后更新于 February 8, 2020,文中内容可能已过时,请谨慎使用。
题目链接
题意简述
有一个字符串 $S[1..n+2]$,告诉你 $\forall 1\le i\le n, S[i..i+2]$(所有长为 $3$ 的子串),求任意一个满足条件的 $S$。
$1\le n\le 2\cdot 10^5$,字符集为大小写字母 + 数字。
简要做法
容易想到需要建图。
但是,如果把每个长为 $3$ 的子串看成点,前后缀匹配看成边,就做不下去了。
正确做法是把每个长为 $2$ 的子串看成点,长为 $3$ 的子串看成边。这样原问题就转化成了求有向图的欧拉路径。(不会这个的话建议自行搜索一下。)
如果每次 dfs 同一个点时都遍历所有出边,度数比较大就会挂。使用 vector 存边的话可以 pop_back()
或者记录一下已经遍历到了哪一条边,使用前向星的话可以像当前弧优化那样修改 head
。
参考代码
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cstdlib>
using namespace std;
const int N = 200010;
const int W = 62 * 62;
int head[N], nxt[N], to[N], cnt;
int n, ind[N], outd[N], tot;
char s[N], ans[N];
int charToInt(char x)
{
if (isdigit(x)) return x - '0';
if (islower(x)) return x - 'a' + 10;
return x - 'A' + 36;
}
char intToChar(int x)
{
if (x < 10) return x + '0';
if (x < 36) return x - 10 + 'a';
return x - 36 + 'A';
}
int wordToInt(char x, char y)
{
return charToInt(x) * 62 + charToInt(y);
}
void add(int u, int v)
{
nxt[++cnt] = head[u];
head[u] = cnt;
to[cnt] = v;
}
void fail()
{
puts("NO");
exit(0);
}
void dfs(int u)
{
for (int& i = head[u]; i; )
{
int v = to[i];
i = nxt[i];
dfs(v);
}
ans[--tot] = intToChar(u % 62);
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
{
scanf("%s", s);
int u = wordToInt(s[0], s[1]);
int v = wordToInt(s[1], s[2]);
++outd[u];
++ind[v];
add(u, v);
}
int oneMoreIn = 0, oneMoreOut = 0;
for (int i = 0; i < W; ++i)
{
if (ind[i] == outd[i]) continue;
if (ind[i] == outd[i] + 1)
{
if (oneMoreIn) fail();
oneMoreIn = i;
}
else if (ind[i] + 1 == outd[i])
{
if (oneMoreOut) fail();
oneMoreOut = i;
}
else fail();
}
if (!oneMoreOut)
{
for (int i = 0; i < W; ++i)
{
if (outd[i])
{
oneMoreOut = i;
break;
}
}
}
tot = n + 2;
dfs(oneMoreOut);
ans[--tot] = intToChar(oneMoreOut / 62);
if (!tot) printf("YES\n%s", ans);
else puts("NO");
return 0;
}
评论正在加载中...如果评论较长时间无法加载,你可以 搜索对应的 issue 或者 新建一个 issue 。