CQOI2014 通配符匹配

本文介绍了一种使用动态规划方法解决字符串S中的通配符(*和?)与另一个字符串T进行匹配的问题。通过划分通配串并定义转移方程,计算出每个T_i是否能被S通配,给出时间复杂度分析。
摘要由CSDN通过智能技术生成

P3167 [CQOI2014] 通配符匹配

题目大意

常见的通配符有以下两种:

  • *:可以匹配 0 0 0个及以上的任意字符
  • ?:可以匹配一个任意字符

给定一个由通配符和小写字母组成的字符串 S S S n n n个由小写字母组成的字符串 T i T_i Ti,求每个 T i T_i Ti能否被通配字符串 S S S通配。

1 ≤ n ≤ 100 1\leq n\leq 100 1n100

字符串长度不超过 1 0 5 10^5 105,通配符个数不超过 10 10 10


题解

依题意,通配符个数不超过 10 10 10,那么我们可以以通配符为界限,将通配串分为若干段。

设通配串能分为 m m m段(也就是有 m − 1 m-1 m1个通配符), o p i op_i opi表示第 i i i段之前的通配符( 0 0 0表示没有, 1 1 1表示? 2 2 2表示*), l e n i len_i leni表示第 i i i段的长度。

对于每个 T k T_k Tk,我们考虑如何判断其能否被通配字符串通配。设 f i , j f_{i,j} fi,j表示第 i i i段的结尾能否匹配上 T k T_k Tk的第 j j j个字符。那么,转移方程为

f i , j = { f i − 1 , j − l e n i o p i = 0 f i − 1 , j − l e n i − 1 o p i = 1 ∑ t = 0 j − l e n i f i − 1 , k o p i = 2 f_{i,j}= \left\{\begin{matrix} f_{i-1,j-len_i} &op_i=0 \\ f_{i-1,j-len_i-1} &op_i=1 \\ \sum\limits_{t=0}^{j-len_i}f_{i-1,k} &op_i=2 \end{matrix}\right. fi,j= fi1,jlenifi1,jleni1t=0jlenifi1,kopi=0opi=1opi=2

用哈希来判断能否转移即可。

答案为 f m , ∣ T k ∣ f_{m,|T_k|} fm,Tk

时间复杂度为 O ( ∣ S ∣ + m ∑ ∣ T k ∣ ) O(|S|+m\sum|T_k|) O(S+mTk)

code

#include<bits/stdc++.h>
using namespace std;
const int N=100000;
const int base=31;
const long long mod=1e9+7;
int T,s1,t1,w1=0,f[15][N+5];
long long bw[N+5],h[N+5],sf[15][N+5];
char s[N+5],t[N+5];
struct node{
	int bg,ed,op;
	long long hsh;
}w[15];
void init(){
	bw[0]=1;
	for(int i=1;i<=N;i++){
		bw[i]=bw[i-1]*base%mod;
	}
}
void solve_s(){
	s1=strlen(s+1);
	w[++w1].bg=1;
	w[w1].op=0;
	for(int i=1;i<=s1;i++){
		if(s[i]=='?'||s[i]=='*'){
			w[w1].ed=i-1;
			w[++w1].bg=i+1;
			if(s[i]=='?') w[w1].op=1;
			else w[w1].op=2;
		}
	}
	w[w1].ed=s1;
	for(int i=1;i<=w1;i++){
		w[i].hsh=0;
		for(int j=w[i].bg;j<=w[i].ed;j++){
			w[i].hsh=(w[i].hsh*base+s[j]-'a'+1)%mod;
		}
	}
}
void solve_t(){
	t1=strlen(t+1);
	for(int i=1;i<=t1;i++){
		h[i]=(h[i-1]*base+t[i]-'a'+1)%mod;
	}
}
long long gthsh(int l,int r){
	return (h[r]-h[l-1]*bw[r-l+1]%mod+mod)%mod;
}
void solve_dp(){
	for(int i=0;i<=w1;i++){
		for(int j=0;j<=t1;j++) sf[i][j]=f[i][j]=0;
	}
	if(gthsh(1,w[1].ed-w[1].bg+1)!=w[1].hsh) return;
	f[1][w[1].ed-w[1].bg+1]=1;
	for(int i=2;i<=w1;i++){
		sf[i-1][0]=f[i-1][0];
		for(int j=1;j<=t1;j++){
			sf[i-1][j]=sf[i-1][j-1]|f[i-1][j];
		}
		int len=w[i].ed-w[i].bg+1;
		for(int j=1;j<=t1;j++){
			if(w[i].op==1){
				if(j-len-1>=0&&gthsh(j-len+1,j)==w[i].hsh){
					f[i][j]=f[i-1][j-len-1];
				}
			}
			else{
				if(j-len>=0&&gthsh(j-len+1,j)==w[i].hsh){
					f[i][j]=sf[i-1][j-len];
				}
			}
		}
	}
}
int main()
{
	init();
	scanf("%s",s+1);
	solve_s();
	scanf("%d",&T);
	while(T--){
		scanf("%s",t+1);
		solve_t();
		solve_dp();
		if(f[w1][t1]) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}
  • 29
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
通配符匹配,通常在计算机编程、正则表达式(Regular Expression)以及文件系统等场景中被用到,它的工作原理主要是用来模糊地匹配字符串或模式。其核心思想是使用特定符号代表零个或多个字符,或者特定类型的字符。 1. `*` 或星号通配符:表示前面的字符可以出现任意次数,包括0次。例如,`a*b` 可以匹配 "ab", "a", ""(空字符串)。 2. `?` 或问号通配符:只允许前面的字符出现一次。即该字符可以出现0次或1次。 3. `[]` 或方括号:用于指定一个字符集,表示匹配其中任何一个字符。比如 `[abc]` 匹配 "a"、"b" 或 "c"。 4. `{}` 或花括号:更复杂的情况可以用来设定精确的数量范围。例如 `{3,5}` 表示前面的字符至少出现3次,但不超过5次。 5. `^` 和 `$` 分别代表行首和行尾,用于限定匹配的起始位置和结束位置。 6. `.` 或点号:匹配除换行符以外的任意单个字符。 7. `\` 转义字符:用于转义特殊字符,使其失去通配功能,如 `\*` 表示匹配实际的星号字符。 在执行通配符匹配时,系统通常会从左到右扫描输入字符串,根据通配符规则尝试找到匹配的部分。当遇到不匹配的地方,就会停止匹配。了解这些基本规则有助于我们编写更灵活的搜索和替换操作。如果你有任何关于具体语言或库中通配符使用的具体问题,请告诉我,我会进一步解释。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值