抄卡组(HNOI2014)

抄卡组(HNOI2014)

题目描述

给出\(n\)个字符串,其中含有通配符\(*\),通配符可以为长度大于等于\(0\)的任意字符串。要求判断这\(n\)个字符串是否有可能为同一个字符串。

输入

第一行包含一个正整数 \(T\),表示了数据组数。
接下来包含 \(T\) 组数据:
每组数据的第一行是一个正整数 \(N\),表示该组数据的字符串个数。
接下来 \(N​\) 行,每行一个字符串,字符串仅包含小写字母、数字、通配符 *

思路

这题有个比较玄学的结论:如果该n个字串的前后缀都能够匹配(即第一个/最后一个通配符之前/之后的部分),且中间至少有一个通配符,那么肯定是可以满足的。此处不给出详细证明,应该感性思考一下,都是没有问题的。

那么计算前后缀的规则比较简单(复杂):如果当前的前缀匹配超出了标准前缀,那么就把标准前缀更新到当前前缀,若匹配失败则返回\(0​\). 后缀同理即可。

bool get(vector<char>str){
	int i=0;
	len=(int)str.size();
	while(i<len&&str[i]!='*'){
		if(i>(int)pre.size()-1)pre.pb(str[i]);
		else if(pre[i]!=str[i])return false;
		i++;
	}
	i=0;
	while(i<len&&str[len-i-1]!='*'){
		if(i>(int)suf.size()-1)suf.pb(str[len-i-1]);
		else if(str[len-i-1]!=suf[i])return false;
		i++;
	}
	return true;
}

接下来就是存在没有通配符的字串的情况,这个时候其实就是把一个没有通配符的串拿出来(这个串就相当于最终卡组了),此处叫它为\(p\)串,那么只要拿另外的\(n-1\)个串与他进行\(KMP\)匹配即可。

怎么\(KMP\)呢?只要把所有\(n-1\)个串按通配符分成若干个子串,然后只要在不改变每个子串的先后顺序,在\(p\)中找到对应的子串即可。

abaaab

*ba*a*

如以上两串,若第一个为\(p\)串,那么以下的两个子串 \(ba\)\(a\),可以分别在\(p\)串中得到匹配,同时匹配的先后顺序保持不变。

不过同时,也要满足前后缀匹配(其实也可以在\(KMP\)中特判前后缀,但个人感觉比较麻烦),可以拿第一种情况方法照搬过来,为什么要匹配前后缀呢?拿这组数据就知道了\(abaaab\),\(ba*ab\),虽然能匹配但不合法。

那么综合以上两种情况就得到最后解发了,总结一下就是:判前缀后缀,若有无通配符的字串,就再将每个其他字符串再KMP匹配。上代码(奇丑无比)

代码

#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l,i##R=r;i<=i##R;i++)
#define DOR(i,r,l) for(int i=r,i##L=l;i>=i##L;i--)
#define loop(i,n) for(int i=0,i##R=n;i<i##R;i++)
#define sf scanf
#define pf printf
#define mms(a,x) memset(a,x,sizeof a)
#define pb push_back
using namespace std;
typedef long long ll;
typedef long double db;
template<typename A,typename B>inline void chkmax(A &x,const B y){if(x<y)x=y;}
template<typename A,typename B>inline void chkmin(A &x,const B y){if(x>y)x=y;}
const int N=1e5+5;
int n;
vector<char>str[N],pre,suf;
bool legal(char c){return (c>='a'&&c<='z')||(c>='0'&&c<='9')||c=='*';}
bool read(int x){
	char c;bool flag=0;str[x].clear();
	while(c=getchar(),!legal(c));
	str[x].pb(c),flag|=(c=='*');
	while(c=getchar(),legal(c))str[x].pb(c),flag|=(c=='*');
	return flag;
}
int len;
bool fpre,fsuf;
bool get(vector<char>str){
	int i=0;
	len=(int)str.size();
	while(i<len&&str[i]!='*'){
		if(i>(int)pre.size()-1){
			pre.pb(str[i]);
		}
		else if(pre[i]!=str[i])return false;
		i++;
	}
	i=0;
	while(i<len&&str[len-i-1]!='*'){
		if(i>(int)suf.size()-1){
			suf.pb(str[len-i-1]);
		}
		else if(str[len-i-1]!=suf[i])return false;
		i++;
	}
	return true;
}
struct Pt2{
	int Next[N*20];
	int lst,id;
	char tmp[N*20];
	void get_nxt(char str[],int len){
	    int j=0;
	    FOR(i,2,len){
	        while(j&&str[i]!=str[j+1])j=Next[j];
	        j+=(str[i]==str[j+1]);
	        Next[i]=j;
	    }
	}
	bool KMP(int len){
		int j=0;
		FOR(i,lst,str[id].size()-1){
			while(j&&str[id][i]!=tmp[j+1])j=Next[j];
			j+=(str[id][i]==tmp[j+1]);
			if(j==len){lst=i+1;return 1;}
		}
		return 0;
	}
	bool check(vector<char>str){
		lst=0;
		loop(i,str.size()){
			if(str[i]!='*'){
				int ct=0;
				while(i<(int)str.size()&&str[i]!='*')tmp[++ct]=str[i],i++;
				tmp[ct+1]='\0';
				get_nxt(tmp,ct);
				if(!KMP(ct))return false;
				i--;
			}
		}
		return true;
	}
	void solve(){
		id=0;
		sf("%d",&n);
		FOR(i,1,n)if(!read(i))id=i;
		fpre=fsuf=0;
		pre.clear(),suf.clear();
		bool flag=0;
		FOR(i,1,n)if(!flag&&!get(str[i]))flag=1;
		if(flag){puts("N");return;}
		if(id){
			FOR(i,1,n){
				if(i==id)continue;
				if(!check(str[i])){puts("N");return;}
			}
		}
		puts("Y");
	}
}Pt_2;
int main(){
	freopen("hs.in","r",stdin);
	freopen("hs.out","w",stdout);
	int T;
	sf("%d",&T);
	while(T--)Pt_2.solve();
	return 0;
}
posted @ 2019-04-18 20:31  Hëinz  阅读(323)  评论(0编辑  收藏  举报