Luogu P3167 [CQOI2014]通配符匹配

[CQOI2014]通配符匹配

题目描述

几乎所有操作系统的命令行界面(CLI)中都支持文件名的通配符匹配以方便用户。最常见的通配符有两个,一个是星号(”“'),可以匹配0个及以上的任意字符:另一个是问号(”?“),可以匹配恰好一个任意字符。现在需要你编写一个程序,对于给定的文件名列表和一个包含通配符的字符串,判断哪些文件可以被匹配。

输入格式

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

输出格式

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

样例 #1

样例输入 #1

*aca?ctc
6
acaacatctc
acatctc
aacacatctc
aggggcaacacctc
aggggcaacatctc
aggggcaacctct

样例输出 #1

YES
YES
YES
YES
YES
NO

提示

对于1 00%的数据

·字符串长度不超过1 00000

· 1 <=n<=100

·通配符个数不超过10

蜜汁纯暴力,写到一半发现不对,把我炸的妈妈都不认识,呜呜呜┭┮﹏┭┮

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

using namespace std;

typedef unsigned long long ull;
ull base[5211314], pre1[5211314], pre2[5211314];
ull t, len1, len2;
char got[5211314], temp[5211314], ask[5211314];
ull spe[521][2], spenum;

bool Work(int got_pos, int ask_pos, int spe_pos) {
	//got_pos通配符字符串的位置
	//ask_pos询问字符串的位置
	//spe_pos第几个通配符 
	//记得判断最后一个字符是不是在最后一位 
	//记得判断最后一个通配符 
	int len;
	int gl = got_pos, gr, al, ar;
	ull gans, aans;
	if (got_pos > len1) return false; 
	if (ask_pos > len2) return false;
	//若超出范围 
	if (spe_pos == spenum + 1) {
		gl = got_pos, gr = len1;
		al = ask_pos, ar = len2;
		gans = pre1[gr] - pre1[gl - 1] * base[gr - gl + 1];
		aans = pre2[ar] - pre2[al - 1] * base[ar - al + 1];
		if (gans == aans) return true;
		else return false;
	} 
	if (spe[spe_pos][0] != 0) flag1 = false;
	else flag1 = true;
	if (spe[spe_pos + 1][0] != 0) flag2 = false;
	else flag2 = true; 
	gr = spe[spe_pos + 1][flag2] - 1;
	//gr 为got上对应匹配的右端点 注意判断spe_pos的值是否为spenum 
	len = ((spe[spe_pos + 1][flag2] - 1) - (spe[spe_pos][flag1] + 1)) + 1;
	//len 为两个通配符之间的字符个数
	if (spe_pos == spenum) len = 0;
	//特判最后一个通配符 
	for (int i = ask_pos; i <= len2 - len + 1; ++ i) {
		if (spe_pos == 0) {
			if (len == 0) {
				//如果字符串第一个就是通配符 
				//** 注意 ** 用不用管flag2??? 
				return Work(got_pos + 1, ask_pos, spe_pos + 1))
				//下一个函数的got_pos注意要加一 
			}
			else {
				//将两个字符串比较 
				al = i;
				ar = al + len - 1;//非通配符串的右节点 
				if (ar > len2) return false;
				//如果通配符子串的长度大于非通配符子串的长度 
				gans = pre1[gr] - pre1[gl - 1] * base[gr - gl + 1];
				aans = pre2[ar] - pre2[al - 1] * base[ar - al + 1];
				if (gans == aans) {
					return Work(spe[spe_pos + 1][falg2], ar + 1, spe_pos + 1);
					//这里下一个函数如果不能返回true, 那么一定返回false 
				}
				else return false;//如果不等于则直接返回false 
			}
		}
		else if (spe_pos == spenum){
			if (flag1 == 1) {
				//当最后一个通配符是"*"的时候 
				if (got_pos - 1 == len1) return true;//若最后一个字符是"*" 
				if (Work(got_pos, i, spe_pos + 1)) return true;
				else return false;
				//将got_pos到len1的子串与i到len2的子串比较 
			}
		}
	} 
	return false;
}

int main() {
	base[0] = 1;
	for (int i = 1; i <= 114514; ++ i) {
		base[i] = base[i - 1] * 13331;
	}
	scanf("%s", temp);
	len1 = strlen(temp);
	for (int i = 0; i < len1; ++ i) {
		got[i + 1] = temp[i];
	}
	for (int i = 1; i <= len1; ++ i) {
		pre1[i] = pre1[i - 1] * base[1] + (ull)got[i];
		if (got[i] == '*') {
			spe[++ spenum][0] = i;
		}
		else {
			spe[++ spenum][1] = i;
		}
	}
	cin >> t;
	while (t --) {
		scanf("%s", temp);
		len2 = strlen(temp);
		for (int i = 0; i < len2; ++ i) {
			ask[i + 1] = temp[i];
		}
		for (int i = 1; i <= len2; ++ i) {
			pre2[i] = pre2[i - 1] * base[1] + (ull)ask[i];
		}
		bool flag = Work(1, 1, 0);
	}
}

优化の暴力,依然大红大黑,又WA又TLE

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>

using namespace std;

typedef unsigned long long ull;
int t, num = 1, trush[114514];
ull base[114514 << 1], ask[114514];
string temp, common;

struct Wildcard_String {
	ull simple_len[114514];
	int simple[114514];
}wild;

void PreTreatMent() {
	int len = 0;
	base[0] = 1;
	for (int i = 1; i <= 114514; ++ i) {
		base[i] = base[i - 1] * 1331;
	}
	for (int i = 0; i < temp.size(); ++ i) {
		if (temp[i] == '*' || temp[i] == '?') {
			wild.simple_len[i - len] = len;
			wild.simple[i - len] = wild.simple[i - 1];
			//将预处理完的状态都转移到每个非通配符串的第一个 
//			cout << i << " " << i - len << endl; 
			len = 0;
		}
		else {
			len ++;
			wild.simple[i] = wild.simple[i - 1] * base[1] + (ull)temp[i];
		}
	}
} 

bool Work(int get_pos, int ask_pos) {
	if (get_pos == temp.size() - 1) {//当通配符串到最后一位 
		if (ask_pos == common.size() || temp[get_pos] == '*') return true;
		//若匹配串也为最后一位或通配符串最后一位为"*" 
		else return false;//若匹配串不为最后一位 
	}
	if (ask_pos == common.size()) return false;
	//此时代表通配符串不为最后一位但匹配串在最后一位 
	if (temp[get_pos] == '?') {
		return Work(get_pos + 1, ask_pos + 1);
		//若为通配符"?",则两个字符串不用管get_pos位置的字符 
	}
	if (temp[get_pos] == '*') {
		//若get_pos位置为通配符"*" 
		for (int i = ask_pos; i < common.size(); ++ i) {
			//将被匹配字符串一一向后匹配 
			if (Work(get_pos + 1, ask_pos)) return true;
		}
		return false;
	}
	//若get_pos位置为普通字符则直接比较
	if (ask_pos + wild.simple_len[get_pos] - 1 >= common.size()) return false;
	//若匹配字符串剩下的长度不够 
	int len = wild.simple_len[get_pos];
	int al = ask_pos, ar = al + len - 1;
	ull gans = wild.simple[get_pos], aans = ask[ar] - ask[al - 1] * base[ar - al + 1];
	//将两个子串哈希值处理出来比较 
	if (gans == aans) return Work(get_pos + len, ar + 1);
	else return false;
}

int main() {
	cin >> temp;
	cin >> t;
	cout << temp.size() << endl; 
	PreTreatMent();
	while (t --) {
		cin >> common;
		for (int i = 0; i < common.size(); ++ i) {
			ask[i] = ask[i - 1] * base[1] + (ull)common[i];
		}
		if (Work(0, 0)) cout << "YES" << endl;
		else cout << "NO" << endl; 
	}
	return 0; 
} 

然后我又改了改,发现还真能过,但按理说复杂度是 \({(10^6)}^{10}\) ,复杂度有一些迷

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>

using namespace std;

typedef unsigned long long ull;
int t, num = 1, trush[114514];
ull base[114514 << 1], ask[114514];
string temp, common;

struct Wildcard_String {
	ull simple_len[114514];
	ull simple[114514];
}wild;

void PreTreatMent() {
	int len = 0;
	base[0] = 1;
	for (int i = 1; i <= 114514; ++ i) 
		base[i] = base[i - 1] * 1331;
	for (int i = 0; i < temp.size(); ++ i) {
		if (temp[i] == '*' || temp[i] == '?') {
			wild.simple_len[i - len] = len;
			wild.simple[i - len] = wild.simple[i - 1];
			//将预处理完的状态都转移到每个非通配符串的第一个 
			len = 0;
		}
		else len ++, wild.simple[i] = wild.simple[i - 1] * base[1] + (ull)temp[i];
	}
	wild.simple_len[temp.size() - len] = len;
	wild.simple[temp.size() - len] = wild.simple[temp.size() - 1];
	return;
} 

bool Work(int get_pos, int ask_pos) {
	if (get_pos >= temp.size()) { //当通配符串到最后一位 
		if (ask_pos >= common.size() || temp[temp.size() - 1] == '*') return true;
		//若匹配串也为最后一位或通配符串最后一位为"*" 
		else return false;//若匹配串不为最后一位 
	}
	if (ask_pos == common.size()) {
		if (get_pos == temp.size() - 1 && temp[temp.size() - 1] == '*') return true;
		//注意特判通配符串最后一个是否为*,因为*可以匹配0个字符 
		else return false;
	}
	//此时代表通配符串不为最后一位但匹配串在最后一位 
	if (temp[get_pos] == '?') {
		return Work(get_pos + 1, ask_pos + 1);
		//若为通配符"?",则两个字符串不用管get_pos位置的字符 
	}
	if (temp[get_pos] == '*') {
		//若get_pos位置为通配符"*" 
		for (int i = ask_pos; i < common.size(); ++ i) {
			//将被匹配字符串一一向后匹配 
			if (i >= common.size()) return false;
			if (Work(get_pos + 1, i)) return true;
		}
		return false;
	}
	//若get_pos位置为普通字符则直接比较
	if (ask_pos + wild.simple_len[get_pos] - 1 >= common.size()) {
		return false;
	}
	//若匹配字符串剩下的长度不够 
	int len = wild.simple_len[get_pos];
	int al = ask_pos, ar = al + len - 1;
	ull gans = wild.simple[get_pos], aans = ask[ar] - ask[al - 1] * base[ar - al + 1];
	//将两个子串哈希值处理出来比较 
	if (gans == aans) return Work(get_pos + len, ar + 1);
	else return false;
}

int main() {
	cin >> temp >> t;
	PreTreatMent();
	while (t --) {
		cin >> common;
		for (int i = 0; i < common.size(); ++ i) {
			ask[i] = ask[i - 1] * base[1] + (ull)common[i];
		}
		if (Work(0, 0)) cout << "YES" << endl;
		else cout << "NO" << endl; 
	}
	return 0; 
} 
posted @ 2023-06-10 22:12  觉清风  阅读(22)  评论(0编辑  收藏  举报