HDU 3901 Wildcard

时间:2021-11-23 10:21:41

题目:Wildcard

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3901

题意:给一个原串(只含小写字母)和一个模式串(含小写字母、?、* ,*号可替换为0到无穷个任意字母,?可替换为一个任意字母),问两个字符串是否匹配。

思路:

  这是经典题吧。。。

  AC自动机。(网上大多代码是错的,用普通kmp是不行的,不知道修改过后可不可以)。

  HDU已经加强数据了,可我前一个代码abcd ?显示是YES居然还过了,看来数据还是有点弱啊。

  *号的处理是很容易的,只需要把模式串以*号为分界分解开来,然后除了最后一段,其他尽量匹配靠前的原串子串就差不多了,比如ab*cd*ef*gh,那么原串必须以ab开头、gh结尾、然后找到最前的cd、再找到ef,如果互相不重叠就OK。

  比较难的是?的处理了,*分解开模式串分别处理。现在,根据?分解,将分解后的字符串(不含?号了)构建AC自动机。

  比如串A:abcd?abc?cd?abcd,构建AC自动机,然后在结束点记录他在串A中的位置。

  HDU 3901 Wildcard红色为根,黄色为每一个结束点,这里的fail指针与普通AC自动机相同

  现在我们创建一个数组cnt,对应原串的每一位,让原串在自动机上跑,如果遇到黄点cnt[当前位置-记录的值+1]++,比如遇到abcd,那说明原串当前位置前四位一定是abcd,满足这一段,而如果有某一个cnt值等于4,就说明他满足了所有段,那么他可以作为串A匹配的起点,a b c d _ a b c _ c d _ a b c d,只有上面这种情况才会使第一个a的cnt值为4,而cnt等于4,当前位就是该次匹配满足的最前位置了。

  上一段理解后,题目就可以得解了,注意细节就是了。

AC代码:

 #include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<set>
#include<map>
#include<list>
#include<stack>
#include<queue>
#include<vector>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
#define lson rt<<1
#define rson rt<<1|1
#define N 100010
#define M 26
#define Mod 1000000007
#define LL long long
#define INF 0x7fffffff
#define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;i++)
#define For(i,f_start,f_end) for(int i=f_start;i<f_end;i++)
#define REP(i,f_end,f_start) for(int i=f_end;i>=f_start;i--)
#define Rep(i,f_end,f_start) for(int i=f_end;i>f_start;i--)
#define MT(x,i) memset(x,i,sizeof(x))
#define gcd(x,y) __gcd(x,y)
const double PI = acos(-); struct Node
{
vector<int> d;
int fail;
int next[M];
}v[N];
int vNum; void Init()
{
v[].fail = -;
FOR(i,,){
v[].next[i]=-;
}
vNum=;
} int create()
{
v[vNum].d.clear();
v[vNum].fail=-;
for(int i=;i<M;i++) v[vNum].next[i]=-;
return vNum++;
} int getPos(char c)
{
return c-'a';
} int add(char *s)
{
int i,ret=,p=;
for(i=;s[i];i++){
if(s[i]=='?'){
if(p!=){
ret++;
v[p].d.push_back(i);
p=;
}
}
int pos = getPos(s[i]);
if(v[p].next[pos]==-) v[p].next[pos]=create();
p = v[p].next[pos];
}
if(p!=){
ret++;
v[p].d.push_back(i);
}
return ret;
} queue<int> q;
void makeFail()
{
q.push();
while(q.size())
{
int p=q.front();
q.pop();
for(int i=;i<M;i++)
{
if(v[p].next[i]!=-)
{
int t=v[p].fail;
while()
{
if(t == -) // p 已经是根了
{
v[v[p].next[i] ].fail = ;
break;
}
else if(v[t].next[i] != -)
{
v[v[p].next[i] ].fail = v[t].next[i];
break;
}
else t=v[t].fail;
}
if(t!=-){
for(int j=;j<v[t].d.size();j++){
v[p].d.push_back(v[t].d[j]);
}
}
q.push(v[p].next[i]);
}
}
}
} int cnt[N];
int match(char *s,char *s1)
{
Init();
int duan = add(s1);
if(duan==) return ; makeFail(); //printf("%s %s %d\n",s,s1,duan); memset(cnt,,sizeof(cnt));
int i,p=;
for(i=;s[i];i++){
int pos = getPos(s[i]);
if(p==-){
p=;
continue;
}
if(v[p].next[pos]==-){
p = v[p].fail;
i--;
continue;
}
p = v[p].next[pos];
for(int j=;j<v[p].d.size();j++){
cnt[i-v[p].d[j]+]++;
if(cnt[i-v[p].d[j]+]==duan) return i-v[p].d[j]+;
}
}
return -;
} char s[N],s1[N],s2[N];
bool solve(char *s,char *s1)
{
int ls=strlen(s),ps=;
int i,len=,flag=;
for(i=;s1[i];i++){
if(s1[i]=='*'){
if(len!=){
s2[len]=;
int qi = match(s+ps,s2);
if(qi==-) return false;
if(flag== && qi!=) return false;
ps += qi + len;
if(ps>ls) return false;
len = ;
}
flag=;
}
else s2[len++]=s1[i];
}
if(len!=){
len--;
for(i=ls-;i>=ps && len>=;i--){
if(s2[len]!='?' && s2[len]!=s[i]) return false;
len--;
}
if(i>=ps && flag== || len>=) return false;
}
return true;
} int main()
{
while(scanf("%s%s",s,s1)!=EOF)
{
printf("%s\n",solve(s,s1)?"YES":"NO");
}
return ;
}