给定\(k\)个被病毒感染了的字符串,知道这\(k\)个字符串本来是按字典序从小到大排列,最后给出一个待复原的字符串\(s\),要求根据上面的\(k\)个被感染的字符串复原\(s\)html
\(k≤50000\)node
拓扑排序&模拟c++
感受有点没读懂或者没思路?spa
那咱们经过思考几个问题来找出思路:.net
由于这\(k\)个字符串本来是按字典序排列的,因此是有序的;被病毒感染只会改变原来字符串的样子,但并不会影响原来的排序!code
因此咱们能够根据这\(k\)个字符串得出原字符\(x\)和感染后的字符\(y\)之间的对应关系!(能够理解为一一对应的映射关系)htm
这里我想到的作法是:双层循环+逐位查询+拓扑排序blog
说白点就是先模拟字典序排序的过程:若是两个字符串的第\(i\)位相等,则判断第\(i+1\)位,直到其中一个字符串完结
在这个过程当中咱们找到一个两个字符不一样的位置,就进行连边和入度累加的操做,以后使用拓扑排序便能获得映射关系
为了方便理解,咱们插入理解一下样例:
先经过模拟字典序排序过程,咱们能够获得以下的字符大小序列:\(c<e<d<a<b\) 和 \(e<a\)
那么映射关系即:\(c->a,e->b,d->c,a->d,b->e\)(注意箭头前的是被感染后的字符,箭头后的是原始字符)
找到了一个感受没有?qwq
\(c<e<d<a<b\)这个便是拓扑序列!而后根据这个拓扑序列咱们就可以找到映射关系!
思路就是如上这么多了,理解后应该就能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<word[i].length()&&l<word[j].length()) { //匹配长度小于两个字符串的最小长度 flag[word[i][l]-'0']=1; //标记一下出现过,后面会用 if(word[i][l]==word[j][l]) l++; //相同则无论 else { flag[word[j][l]-'0']=1; in[word[j][l]-'0']++; //不一样说明i串位置字符的字典序先于j串位置字符的字典序 add(word[i][l]-'0',word[j][l]-'0'); //连边+入度累加 break; //找到一位后面的就不用比较了 } } } }
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 <bits/stdc++.h> using namespace std; queue<int> q; map<int,int> 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[i].length()&&l<word[j].length()) { flag[word[i][l]-'0']=1; if(word[i][l]==word[j][l]) l++; else { flag[word[j][l]-'0']=1; in[word[j][l]-'0']++; add(word[i][l]-'0',word[j][l]-'0'); break; } } } } cin>>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<word[n+1].length();i++) { //若是对应不完,那也是一种错误状况 if(ans[word[n+1][i]-'0']==0) { printf("0"); return 0; } } for(register int i=0;i<word[n+1].length();i++) { cout<<char(ans[word[n+1][i]-'0']); } return 0; }