您的位置:首页 > 其它

【HDOJ】3901 Wildcard

2016-03-29 23:10 225 查看
1. 题目描述
有两个长度为$10^5$的字符串,其中一个仅包含小写字母,称之为源串;另一个包含?与*通配符的模式串,并且通配符的个数不超过10。求模式串是否能匹配源串?

2. 基本思路
这其实是一道RegularExpression-Matching问题。一般化的解法就是构建自动机,然后去在这个自动机上搜索源串,是否被接收即为答案。这个一般化方法的时间复杂度为$O(|x||y|)$。显然,这里不适用。题目中的限定条件通配符个数不超过10使得,不需要构建完整的自动机。我们可以将模式串$d$改写为
$d_0 \cdot (?|*) \cdot d_1 \cdot (?|*) \cdots d_k$,其中$d_0, d_1, \cdots d_k$可以为$\varepsilon$。
因此,我们可以换一个思路。首先,利用kmp算法我们可以求得子串$d_i, i \in [0, k]$在源串$s$上的匹配位置,称之为end-point。
则对通配符分情况讨论:
(1) ?:仅匹配一个字符,那么d_k匹配的有效的end-point的下一个位置仍然可以匹配;
(2) *:可以匹配任意个字符,那么d_k首次匹配的end-point的后续位置都可以匹配。
注意,这里的有效有特殊含义,有效不仅要保证这次的子串$d_i$匹配end-point,同时一定要满足上一次的位置$end-point - |d_i|$同样是可以匹配的。
因此,算法的框架就变成了:
(1) 对模式串进行分割,分割点为?或*;
(2) 在源串中使用kmp算法搜索子串的end-point并且标记;
(3) 利用前次的匹配标记数组$ep$与kmp的标记$visit$共同确定这次的匹配标记;
(4) 对长度为0的字符串单独讨论;
(5) 对不含通配符的模式串,直接strcmp。

3. 代码

/* 3901 */
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#include <deque>
#include <bitset>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <cstring>
#include <climits>
#include <cctype>
#include <cassert>
#include <functional>
#include <iterator>
#include <iomanip>
using namespace std;
//#pragma comment(linker,"/STACK:102400000,1024000")

#define sti                set<int>
#define stpii            set<pair<int, int> >
#define mpii            map<int,int>
#define vi                vector<int>
#define pii                pair<int,int>
#define vpii            vector<pair<int,int> >
#define rep(i, a, n)     for (int i=a;i<n;++i)
#define per(i, a, n)     for (int i=n-1;i>=a;--i)
#define clr                clear
#define pb                 push_back
#define mp                 make_pair
#define fir                first
#define sec                second
#define all(x)             (x).begin(),(x).end()
#define SZ(x)             ((int)(x).size())
#define lson            l, mid, rt<<1
#define rson            mid+1, r, rt<<1|1

#ifndef ONLINE_JUDGE
const int maxn = 100;
#else
const int maxn = 1e5+5;
#endif
const int maxm = 13;
char s[maxn], d[maxn];
bool ep[maxm][maxn];
bool visit[maxn];
char ss[maxn];
int nxt[maxn], slen, dlen;

void getnext(char *s, int len) {
int i, j;

nxt[0] = -1;
i = 0;
j = -1;
while (i < len) {
if (j==-1 || s[i]==s[j]) {
++i;
++j;
nxt[i] = j;
} else {
j = nxt[j];
}
}
}

void findEndPoint(char *ss, int len) {
int i = 0, j = 0;

getnext(ss, len);
memset(visit, false, sizeof(visit));
while (i < slen) {
if (s[i] == ss[j]) {
++i;
++j;
} else {
j = nxt[j];
if (j == -1) {
j = 0;
++i;
}
}
if (j == len) {
visit[i] = true;
}
}
}

void solve() {
slen = strlen(s);
dlen = strlen(d);
int gid = 0;
int i = 0, l = 0;

{
// fast check
bool flag = true;
rep(k, 0, dlen) {
if (d[k]=='?' || d[k]=='*') {
flag = false;
break;
}
}

if (flag) {
if (strcmp(s,d)==0)
puts("YES");
else
puts("NO");
return ;
}
}

memset(ep, false, sizeof(ep));
ep[gid++][0] = true;
while (i < dlen) {
if (islower(d[i])) {
ss[l++] = d[i++];
continue;
}

ss[l] = '\0';
if (d[i]=='?') {
if (l == 0) {
rep(i, 0, slen+1)
ep[gid][i+1] = ep[gid-1][i];
} else {
findEndPoint(ss, l);
rep(i, 0, slen+1) {
if (ep[gid-1][i] && visit[i+l]) {
ep[gid][i+l+1] = true;
}
}
}
} else if (d[i]=='*') {
if (l == 0) {
int fir = slen+1;
rep(i, 0, slen+1) {
if (ep[gid-1][i]) {
fir = i;
break;
}
}

rep(i, fir, slen+1)
ep[gid][i] = true;
} else {
findEndPoint(ss, l);
int fir = slen + 1;
rep(i, 0, slen+1) {
if (ep[gid-1][i] && visit[i+l]) {
fir = i + l;
break;
}
}

rep(i, fir, slen+1)
ep[gid][i] = true;
}
}

l = 0;
++gid;
++i;
}

if (l > 0) {
ss[l] = '\0';
findEndPoint(ss, l);
rep(i, 0, slen+1) {
if (ep[gid-1][i] && visit[i+l]) {
ep[gid][i+l] = true;
}
}
++gid;
}

if (ep[gid-1][slen])
puts("YES");
else
puts("NO");
}

int main() {
ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif

while (scanf("%s", s)!=EOF) {
scanf("%s", d);
solve();
}

#ifndef ONLINE_JUDGE
printf("time = %d.\n", (int)clock());
#endif

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: