牛客练习->字符串Hash->通配符匹配(DP+Hash)

题目链接

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

几乎所有操作系统的命令行界面(CLI)中都支持文件名的通配符匹配以方便用户。最常见的通配符有两个,一个是星号(“”’),可以匹配0个及以上的任意字符:另一个是问号(“?”),可以匹配恰好一个任意字符。

现在需要你编写一个程序,对于给定的文件名列表和一个包含通配符的字符串,判断哪些文件可以被匹配。

输入描述:

第一行是一个由小写字母和上述通配符组成的字符串。
第二行包含一个整数n,表示文件个数。
接下来n行,每行为一个仅包含小写字母字符串,表示文件名列表。

输出描述:

输出n行,每行为“YES”或“NO”,表示对应文件能否被通配符匹配。

示例1

输入

*aca?ctc
6
acaacatctc
acatctc
aacacatctc
aggggcaacacctc
aggggcaacatctc
aggggcaacctct

输出

YES
YES
YES
YES
YES
NO

对于1 00%的数据
字符串长度不超过1 00000
1 <=n<=100
通配符个数不超过10


用dp[i][j]表示主串S从第1个字符到第i个通配符间的子串是否与匹配串T从第1个字符到第j个字符间的子串相匹配,1表示匹配。

由于主串的最后一个字符串不一定是通配符'*'或'?',所以在主串后加一个通配符'?,匹配串后加一个任意的字符,这样问题的解就可以用dp[cnt][LT](cnt为主串通配符的个数,LT为匹配串的长度)来表示。

不含通配符的字符串匹配利用Hash判断

AC代码:

//CSDN博客:https://blog.csdn.net/qq_40889820
#include<iostream>
#include<sstream>
#include<fstream>
#include<algorithm>
#include<string>
#include<cstring>
#include<iomanip>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<map>
#include<set>
#define mem(a,b) memset(a,b,sizeof(a))
#define random(a,b) (rand()%(b-a+1)+a)
#define ull unsigned long long
#define e 2.71828182
#define Pi 3.141592654
using namespace std;
const int MAXN=1e5+10;
const int Base=1e9+7;
char S[MAXN],T[MAXN];
int n,LS,LT,cnt;
int dp[13][MAXN];
int pos[13];//pos[i]表示主串中第i个通配符的位置 
ull haS[MAXN],haT[MAXN],power[MAXN],htmp;
inline ull hash1(int l,int r)
{
	if(l>r) return 0;
	return haS[r]-haS[l-1]*power[r-l+1];
}
inline ull hash2(int l,int r)
{
	if(l>r) return 0;
	return haT[r]-haT[l-1]*power[r-l+1];
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>(S+1)>>n;
	LS=strlen(S+1);
	S[++LS]='?';//在主串末尾加个'?' 
	
	power[0]=1;
	for(int i=1;i<=MAXN;++i) power[i]=power[i-1]*Base;
	for(int i=1;i<=LS;++i)
	{
		if(S[i]=='*'||S[i]=='?') pos[++cnt]=i;
		haS[i]=haS[i-1]*Base+S[i]-'a'+1;
	}	
	while(n--)
	{
		cin>>(T+1);
		LT=strlen(T+1);
		T[++LT]='.';//在匹配串末尾任意加个字符 
		haT[0]=0;
		for(int i=1;i<=LT;++i) 
			haT[i]=haT[i-1]*Base+T[i]-'a'+1;
		mem(dp,0);dp[0][0]=1;
		
		for(int i=0;i<=cnt-1;++i)
		{
			if(S[pos[i]]=='*')//'*'可匹配0个至任意多个字符 
			{
				for(int j=1;j<=LT;++j)
				if(dp[i][j-1]) dp[i][j]=1;
			}
			htmp=hash1(pos[i]+1,pos[i+1]-1);
			for(int j=0;j<=LT;++j)
			{
				if(dp[i][j]&&htmp==hash2(j+1,j+pos[i+1]-pos[i]-1))//pos[i+1]-1-(pos[i]+1)+1
				{
					if(S[pos[i+1]]=='?') dp[i+1][j+pos[i+1]-pos[i]]=1;//'?'可以与任意一个字符匹配 
					else dp[i+1][j+pos[i+1]-pos[i]-1]=1;
				}
			} 
		}
		if(dp[cnt][LT]) cout<<"YES\n";
		else cout<<"NO\n";
	}
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值