FZU 1926 填空 (字符串处理_KMP)

题目链接: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原创,但可以转载,因为我们是兄弟。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值