【CF1476E】Pattern Matching
题目
题目链接:https://codeforces.com/problemset/problem/1476/E
给定 \(n\) 个模式串 \(p_i\) 和 \(m\) 个字符串 \(s_i\),其中 \(p_i\) 两两不同。每个模式串和字符串都包含 \(k\) 个字符。其中模式串中可以含通配符(用下划线表示,可以匹配任何字符),剩余字符都为小写英文字母。同时给定 \(n\) 个数 \(mt_i\),要求重新排列模式串使得 \(s_j\) 匹配到的第一个模式串为 \(p_{mt_j}\)。请判断是否存在排列方案满足如上要求,能的话请输出方案。
\(n,m\leq 10^5;k\leq 4\)。
思路
挺无聊的一道缝合题。。。
发现 \(k\leq 4\),也就是每一个字符串 \(s_i\) 最多只有 \(16\) 个模式串 \(p\) 可以匹配上。所以我们可以把每一个模式串标号,对于每一个字符串枚举所有能和它匹配的模式串,要求这些模式串的编号都必须大于 \(mt_i\) 的编号。
那就直接上拓扑排序即可。匹配可以不用 Tire,直接看成一个 27 进制数用数组存即可。\(27^4\leq 6\times 10^5\)。
时间复杂度 \(O(2^knk)\)。
代码
#include <bits/stdc++.h>
#define pYES printf("YES\n");
#define pYes printf("Yes\n");
#define pyes printf("yes\n");
#define pNO printf("NO\n");
#define pNo printf("No\n");
#define pno printf("no\n");
#define mp make_pair
#define ST first
#define ND second
using namespace std;
typedef long long ll;
typedef long double ld;
const int N=100010,M=600010;
int n,m,k,tot,head[N],id[M],deg[N],ans[N];
char s[N][6],t[6];
struct edge
{
int next,to;
}e[N*16];
void add(int from,int to)
{
e[++tot]=(edge){head[from],to};
head[from]=tot; deg[to]++;
}
void topsort()
{
tot=0;
queue<int> q;
for (int i=1;i<=n;i++)
if (!deg[i]) q.push(i);
while (q.size())
{
int u=q.front(); q.pop();
ans[++tot]=u;
for (int i=head[u];~i;i=e[i].next)
{
int v=e[i].to;
deg[v]--;
if (!deg[v]) q.push(v);
}
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d%d",&n,&m,&k);
for (int i=1;i<=n;i++)
{
scanf("%s",s[i]+1);
int p=0;
for (int j=1;j<=k;j++)
p=p*27+((s[i][j]=='_')?0:(s[i][j]-'a'+1));
id[p]=i;
}
for (int i=1,x;i<=m;i++)
{
scanf("%s",t+1);
scanf("%d",&x);
int q=0;
for (int j=1;j<=k;j++)
q=q*27+((s[x][j]=='_')?0:(s[x][j]-'a'+1));
bool flag=0;
for (int j=0;j<(1<<k);j++)
{
int p=0;
for (int l=1;l<=k;l++)
p=p*27+((j&(1<<(l-1)))?0:(t[l]-'a'+1));
if (id[p] && p!=q) add(id[q],id[p]);
else if (p==q) flag=1;
}
if (!flag) { pNO; return 0; }
}
topsort();
if (tot<n) { pNO; return 0; }
pYES;
for (int i=1;i<=n;i++)
printf("%d ",ans[i]);
return 0;
}