病毒Virus
一本通P1396 病毒Virus
题目简述
给定\(k\)个被病毒感染了的字符串,知道这\(k\)个字符串原本是按字典序从小到大排列,最后给出一个待复原的字符串\(s\),要求根据上面的\(k\)个被感染的字符串复原\(s\)
- 数据范围
\(k≤50000\)
所需算法
拓扑排序&模拟
前置知识
如果想了解拓扑排序欢迎来踩我的另一篇博客呀qvq
解题思路
感觉有点没读懂或者没思路?
那我们通过思考几个问题来找出思路:
- \(k\)个被感染的字符串能为我们带来什么?
因为这\(k\)个字符串原本是按字典序排列的,所以是有序的;被病毒感染只会改变原来字符串的样子,但并不会影响原来的排序!
所以我们可以根据这\(k\)个字符串得出原字符\(x\)和感染后的字符\(y\)之间的对应关系!(可以理解为一一对应的映射关系)
- 怎么找到这种映射关系?
这里我想到的做法是:双层循环+逐位查询+拓扑排序
说白点就是先模拟字典序排序的过程:如果两个字符串的第\(i\)位相等,则判断第\(i+1\)位,直到其中一个字符串完结
在这个过程中我们找到一个两个字符不同的位置,就进行连边和入度累加的操作,之后使用拓扑排序便能得到映射关系
- 怎么想到的拓扑排序?
为了方便理解,我们插入理解一下样例:
-
那么映射关系即:\(c->a,e->b,d->c,a->d,b->e\)(注意箭头前的是被感染后的字符,箭头后的是原始字符)
找到了一个感觉没有?qwq
\(c
代码Code
思路就是如上这么多了,理解后应该就能A掉这道题了,只是敲代码需要细心一点
现在先放出每个关键步骤的代码段(有注释),最后会放上完整的AC代码(无注释)
- 模拟字典序排序
for(register int i=1;i<=n;i++) { //循环1-n个字符串,这里的n相当于题目中的k(个人习惯啦qwq)
for(register int j=i+1;j<=n;j++) { //循环在i之后的字符串
int l=0; //l枚举字符串的位置
while(l
- 拓扑排序
for(register char i='a';i<='z';i++) { //如果这个字符出现过且入度为0(即字典序最先),就入队
if(flag[i-'0']==1&&in[i-'0']==0) q.push(i-'0');
}
while(!q.empty()) {
int x=q.front();
q.pop();
if(ans[x]!=0) { //这里是判断一个感染字符对应多个原始字符的错误情况
printf("0");
return 0;
}
ans[x]=sum; //注意ans是一个整型map
sum++;
for(register int i=head[x];i;i=e[i].net) { //遍历所有字典序在x之后的字符
int v=e[i].to;
in[v]--;
if(in[v]==0) q.push(v);
}
}
- 完整代码
#include
using namespace std;
queue q;
map ans;
string word[50002];
int n,tot,sum=97,in[201],head[201],flag[201];
struct node {
int to,net;
} e[50001];
inline void add(int u,int v) {
e[++tot].to=v;
e[tot].net=head[u];
head[u]=tot;
}
int main() {
scanf("%d",&n);
for(register int i=1;i<=n;i++) {
cin>>word[i];
}
for(register int i=1;i<=n;i++) {
for(register int j=i+1;j<=n;j++) {
int l=0;
while(l>word[n+1];
for(register char i='a';i<='z';i++) {
if(flag[i-'0']==1&&in[i-'0']==0) q.push(i-'0');
}
while(!q.empty()) {
int x=q.front();
q.pop();
if(ans[x]!=0) {
printf("0");
return 0;
}
ans[x]=sum;
sum++;
for(register int i=head[x];i;i=e[i].net) {
int v=e[i].to;
in[v]--;
if(in[v]==0) q.push(v);
}
}
for(register int i=0;i