病毒Virus

病毒Virus

一本通P1396 病毒Virusphp

題目簡述

給定\(k\)個被病毒感染了的字符串,知道這\(k\)個字符串本來是按字典序從小到大排列,最後給出一個待復原的字符串\(s\),要求根據上面的\(k\)個被感染的字符串復原\(s\)html

  • 數據範圍

\(k≤50000\)node


所需算法

拓撲排序&模擬c++

前置知識

若是想了解拓撲排序歡迎來踩個人另外一篇博客呀qvq算法


解題思路

感受有點沒讀懂或者沒思路?spa

那咱們經過思考幾個問題來找出思路:.net

  • \(k\)個被感染的字符串能爲咱們帶來什麼?

由於這\(k\)個字符串本來是按字典序排列的,因此是有序的;被病毒感染只會改變原來字符串的樣子,但並不會影響原來的排序!code

因此咱們能夠根據這\(k\)個字符串得出原字符\(x\)和感染後的字符\(y\)之間的對應關係!(能夠理解爲一一對應的映射關係)htm

  • 怎麼找到這種映射關係?

這裏我想到的作法是:雙層循環+逐位查詢+拓撲排序blog

說白點就是先模擬字典序排序的過程:若是兩個字符串的第\(i\)位相等,則判斷第\(i+1\)位,直到其中一個字符串完結

在這個過程當中咱們找到一個兩個字符不一樣的位置,就進行連邊和入度累加的操做,以後使用拓撲排序便能獲得映射關係

  • 怎麼想到的拓撲排序?

爲了方便理解,咱們插入理解一下樣例:

  1. 先經過模擬字典序排序過程,咱們能夠獲得以下的字符大小序列:\(c<e<d<a<b\)\(e<a\)

  2. 那麼映射關係即:\(c->a,e->b,d->c,a->d,b->e\)(注意箭頭前的是被感染後的字符,箭頭後的是原始字符)

找到了一個感受沒有?qwq

\(c<e<d<a<b\)這個便是拓撲序列!而後根據這個拓撲序列咱們就可以找到映射關係!


代碼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<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;
}
相關文章
相關標籤/搜索