1476E.Pattern Matching(字典树+拓扑排序)
您将得到n个模式p1,p2,…,pn和m个字符串s1,s2,…,sm。每个模式pi包含k个字符,这些字符可以是小写拉丁字母或通配符(用下划线表示)。所有模式都是成对的。每个字符串sj包含k个小写拉丁字母。
如果bi从1到k中的每个i是通配符或bi = ai,则字符串a与模式b匹配。
要求您以第j个字符串匹配的第一个模式为p [mtj]的方式重新排列模式。您可以保留模式顺序不变。
可以进行这样的重新排列吗?如果可以,则打印任何有效订单。
题意不太好懂,懂了之后就是常规套路,从哪里跌倒就从哪里爬起来。
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+100; int n,m,k; string p[maxn]; struct node { string s; int tk,p; bool operator < (const node &r) const { return tk<r.tk; } }Node[maxn]; vector<int> g[maxn]; vector<int> l[maxn]; int inDegree[maxn]; int Trie[maxn][30]; int tot=0; void insert (int i,int pp,int rt) { //p表示插入起点 if (pp==p[i].size()) { g[rt].push_back(i); return; } if (p[i][pp]=='_') { for (int j=0;j<26;j++) { if (!Trie[rt][j]) Trie[rt][j]=++tot; insert(i,pp+1,Trie[rt][j]); } } else { int tt=p[i][pp]-'a'; if (!Trie[rt][tt]) Trie[rt][tt]=++tot; insert(i,pp+1,Trie[rt][tt]); } } int find (int k) { int rt=0; for (int i=0;i<Node[k].s.size();i++) { int x=Node[k].s[i]-'a'; if (!Trie[rt][x]) return 0; rt=Trie[rt][x]; } l[k]=g[rt]; return 1; } int main () { scanf("%d%d%d",&n,&m,&k); for (int i=1;i<=n;i++) cin>>p[i]; for (int i=1;i<=m;i++) cin>>Node[i].s>>Node[i].tk,Node[i].p=i; for (int i=1;i<=n;i++) insert(i,0,0); for (int i=1;i<=m;i++) { int x=find(i); if (x==0) { return printf("NO"),0; } } //每个字符串有对应的匹配集合l(i) //每个字符串第一个匹配的串应当是p(tk) //每个字符串的匹配集合,p(tk)和剩下的串连单向边 //做拓扑排序 for (int i=0;i<1e6;i++) g[i].clear(); for (int i=1;i<=m;i++) { int f=0; for (int j=0;j<l[i].size();j++) if (l[i][j]==Node[i].tk) f=1; if (!f) return printf("NO\n"),0; for (int j=0;j<l[i].size();j++) { if (l[i][j]==Node[i].tk) continue; g[Node[i].tk].push_back(l[i][j]); inDegree[l[i][j]]++; } } vector<int> ans; queue<int> q; for (int i=1;i<=n;i++) if (inDegree[i]==0) q.push(i); while (q.size()) { int u=q.front(); q.pop(); ans.push_back(u); for (int i=0;i<g[u].size();i++) if (--inDegree[g[u][i]]==0) q.push(g[u][i]); } if (ans.size()!=n) return printf("NO\n"),0; printf("YES\n"); for (int i=0;i<ans.size();i++) printf("%d ",ans[i]); }