ch_g

ECUST_ACMer —— ch_g
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

[EOJ][382][Match Maker][KMP]

Posted on 2011-04-27 23:44  ch_g  阅读(360)  评论(2编辑  收藏  举报
/*
题目:Match Maker
题目来源:EOJ 382
题目难度:中等偏难
题目内容:KMP(模糊匹配)
给出一个模板串和一系列待匹配串,长度小于等于100000。
模板串中含有的字符包括'0'-'9'、'*'、'#'。其中'*'匹配偶数个字符,
'#'匹配奇数个字符;如"1*3",匹配"1223",不匹配"123"。存在连续多
个'*''#'时,匹配字符个数只看奇偶,如"1*#3"等价于"1#3","1**3"等
价于"1*3"。判断后面给出的字符串是否匹配模板串。
解题思路:
把模板串按'*''#'分割开来,并以'*''#'为前缀。比如"1212*34534#78"
分割为"1212"+"*34534"+"#78"。然后每一小段分别建立fail指针,建
fail指针时不必考虑'*''#'。待匹配串从第一小段开始一段段匹配,当
某段匹配成功时,还需要判断前缀是否符合'*''#'的奇偶性。当所有的
小段都匹配成功时,才算完全匹配。比如字符串"12121345345347887878"
匹配刚才的模板串
匹配方式: "1212" "*34534" "#78"
"1212" "134534534" "7887878"
注意的细节:
给每一小段建立fail指针时,不需要开多个数组,只要用一个即可。
并且在最后一个字符的后面再建一个指针,这样匹配成功后判断出不符
合奇偶条件时可以继续匹配。
比如 模板串 :1 2 1 2 * 3 4 5 3 4 # 7 8
fail指针:0 1 1 2 3 0 1 1 1 2 3 0 1 1
另外,匹配的时候,先把首尾连续的数字串匹配掉,这样可以做到精确
匹配。比如"1212*34534#78"和"12121345345347887878"匹配的时候,
可能匹配的方式为
"1212" "*34534" "#78"
"1212" "134534534" "78878" "78"
待匹配串多出了一个"78",这时就会判断为不能完全匹配
先把首尾的"1212"和"78"匹配掉,剩下"*34534#"和"13453453478878"
匹配时就不会有问题了。
做题日期:2011.4.27
*/
#include
<cstdio>
#include
<cstdlib>
#include
<climits>
#include
<iostream>
#include
<algorithm>
#include
<cstring>
#include
<string>
#include
<queue>
#include
<map>
#include
<vector>
#include
<bitset>
#include
<cmath>
#include
<set>
#include
<utility>
#include
<ctime>
#define sqr(x) ((x)*(x))
using namespace std;

const int N = 100010;
char pat[N], str[N];
int fail[N];

int makefail(char *t, int fail[]) {
--t; int i, j;
for (i = 1, j = 0; isdigit(t[i]); ++i, ++j) {
fail[i]
= j;
while (j > 0 && t[i] != t[j]) j = fail[j];
if (!isdigit(t[i + 1])) fail[i + 1] = j + 1;
}
return i;
}

int kmp(char *s, char *t, int fail[], int lim, int flag, int &x) {
--s; --t;
for (int i = 1, j = 1; s[i]; ++i, ++j) {
while (j > 0 && s[i] != t[j]) j = fail[j];
if (!isdigit(t[j + 1])) {
int z = i - j;
if (z >= lim && z % 2 == flag) {
x
+= j;
return i;
}
j
= fail[j + 1] - 1;
}
}
return -1;
}

bool gao() {
int lim, flag, i = 0, j = 0;
int ls = strlen(str);
int lt = strlen(pat);
while (isdigit(pat[j]))
if (str[i++] != pat[j++])
return false;
while (j < lt && isdigit(pat[lt - 1]))
if (str[--ls] != pat[--lt])
return false;
while (j < lt) {
for (lim = 0; j < lt && !isdigit(pat[j]); ++j) {
lim
+= (pat[j] == '#' ? 1 : 2); //本来以为还有长度要求的
flag = lim & 1;
}
if (j == lt) return (ls - i) % 2 == flag;
int t = kmp(str + i, pat + j, fail + j, 0, flag, j);
if (t == -1) return false;
i
+= t;
}
return ls - i == 0;
}

bool solve(int cas) {
int id = 1;
scanf(
"%s", pat);
int len = strlen(pat);
memset(fail,
0, sizeof(fail));
for (int i = 0; i < len; )
i
+= makefail(pat + i, fail + i);
//for (int i = 1; i <= len + 1; ++i)
// printf("%d ", fail[i]);
//puts("");
while (scanf("%s", str)) {
if (str[0] == 'E') return true;
if (str[0] == 'Q') return false;
printf(
"%d.%d. %s\n", cas, id++, gao() ? "match" : "not");
}
return false;
}

int main(){
#ifndef ONLINE_JUDGE
freopen(
"D:\\in.txt", "r", stdin);
#endif
int cas = 1;
while (solve(cas++));
return 0;
}