题目链接:http://acm.fzu.edu.cn/problem.php?pid=1926
题目大意:一题关于字符串匹配的中文题,给出一篇文章,由单词组成,再给定一个句子,句子中包含“_”,“_"可以任意匹配一个单词,问这个句子是否出现在文章中。
解题思路:这题可用KMP解。我的做法是为每个单词编个号,然后根据编号去匹配,"_"可任意匹配。但是这样明显慢,其实可以分两个层次匹配,如果我们比较的文章和句子中的两个单词完全相同(用strcmp比较就好),则匹配下一个单词,不用编号。
但是这题我有疑问,我之前写的代码AC了但是不符合逻辑。我在求next数组时,判断匹配是写if (j == -1 || str[i] == str[j] || str[i] == "_" || str[j] == "_") xxooo,而模式串与匹配串匹配时不对称,隐隐觉得有问题。当文章为 a a a b b b c,匹配串为a _ b c,就会出现问题,我输出YES,,实际情况肯定是NO。这是因为匹配串的next数组为 -1,0,1,2,所以在匹配到a a a b b b和a _ b c时出现失配,然后回嗍到a _ b去匹配,这时发现明显错了,糗。若无其事地提交下竟然AC,这不是误人子弟吗?后来想一想,只要记录“_"所匹配的那个单词就可以解决。大家可以看下面的代码。这题应该是数据太弱了,导致许多人过的代码都有错误。
测试数据:
3
10
2
1 5 2
5 9 3
10
3
1 5 3
1 5 2
1 5 1
10
5
1 4 2
2 4 3
1 2 3
5 6 1
5 9 3
代码:
#include <stdio.h>
#include <string.h>
#include <string>
#include <map>
using namespace std;
#define MAX 1100
int arr[MAX],n,m,kind,num; //hash的时候,"_"为0,其他>0
int match[MAX],next[MAX];
char fuck[MAX][100];
char str[MAX][100],dir[MAX][100];
int Ok(char *s1,char *s2) {
return strcmp(s1,s2) == 0;
}
void GetNext(char match[][100],int m) {
int i,j;
i = 0,j = -1;
next[0] = -1;
while (i < m) {
if (j == -1 || Ok(match[i],match[j])
|| (Ok(match[j],"_") && Ok(match[i],fuck[j])) || Ok(match[i],"_")) {
i++,j++,next[i] = j;
if (Ok(match[i],"_")) strcpy(fuck[i],match[j]);
}
else j = next[j];
}
}
int KMP(char dir[][100],int n,char match[][100],int m){
int i,j;
i = j = 0;
while (i < n) {
if (j == -1 || Ok(dir[i],match[j]) || Ok(match[j],"_"))
i++,j++;
else j = next[j];
if (j == m) return 1;
}
return 0;
}
int main()
{
int i,j,k,t;
int flag,cas = 0;
scanf("%d",&t);
while (t--) {
n = -1,kind = 0;
while (scanf("%s",dir[++n]),dir[n][0] != '@');
printf("Case %d:\n",++cas);
scanf("%d",&num);
while (num--) {
m = -1,flag = 1;
while (scanf("%s",str[++m]),str[m][0] != '@');
GetNext(str,m);
flag = KMP(dir,n,str,m);
if (flag) printf("YES\n");
else printf("NO\n");
}
}
}
本文ZeroClock原创,但可以转载,因为我们是兄弟。