原题
大佬链接:https://blog.csdn.net/clover_hxy/article/details/72779023
这一题主要是说,如果一个字符串中含有*的话,那么它只要比左右两头就行了,如果存在一个字符串没有,那么就需要把所有的字符串与它比较了。
自己写的kmp匹配疯狂LTE,看了下大佬用hash比两端过,最后就是要注意的是数组和p的大小需要开大一点, 否则卡数据(当时到处测bug测晕了,才发现是p和MX开小了)。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MX 10500000
#define p 20001001019
#define ull unsigned long long
using namespace std;
ull mi[MX],has[MX];
char ch[MX],s[MX];
struct node{
int l,r,x,y,len,cnt;
}a[MX];
bool cmp1(node &a,node &b){
return a.x<b.x;
}
bool cmp2(node &a,node &b){
return a.y<b.y;
}
int main()
{
// freopen("input.txt","r",stdin);
int T,n,m,mx,pos;
scanf("%d",&T);
mi[0]=1;
for( int i=1 ; i<=MX-1 ; i++ )
mi[i]=mi[i-1]*p;
while( T-- ){
m=0,mx=MX;
scanf("%d",&n);
for( int i=1 ; i<=n ; i++ ){
scanf("%s",s+1);
int len=strlen(s+1),l=len+1,r=0;
for( int j=1 ; j<=len ; j++ ){
ch[m+j]=s[j];
if( j==1 ) has[m+j]=mi[j]*s[j];
else has[m+j]=has[m+j-1]+mi[j]*s[j];
if( s[j]=='*' ){
l=min(l,j-1),r=max(r,j+1);
a[i].cnt++;
}
}
a[i].l=m+1,a[i].r=m+len,a[i].len=len; // l,r是存第i个字符串在ch中的范围
if( l!=len+1 ) // x与y是存第i个字符串的左右两边直到遇见第一个*的长度
a[i].x=l,a[i].y=len+1-r;
else{
a[i].x=a[i].y=len;
mx=min(mx,len);
if( mx==len )
pos=i; // 若该字符串中没有*,则它是模板串,用pos存最小模板串的标号
}
m+=len;
}
int pd=1;
for( int i=1 ; i<=n ; i++ ){
if( a[i].len-a[i].cnt>mx ){ // 任何一个字符串都不可以比最小模板串有更多的字符
pd=0;
break;
}
}
if( !pd ){
printf("N\n");
continue;
}
if( mx!=MX ){ // 若有模板串,则把所有的字符串和最小模板串匹配
for( int i=1 ; i<=n ; i++ ){
int t=a[i].l-1,l=a[pos].l;
for( int j=1 ; j<=a[i].len ; j++ ){
if( ch[j+t]=='*' )
continue;
while( ch[t+j]!=ch[l] && l<=a[pos].r ) l++;
if( ch[t+j]!=ch[l] ){
pd=0;
break;
}
l++;
}
if( !pd )
break;
}
}
if( !pd ){
printf("N\n");
continue;
} // 记住,以上所有的只是确定了所有的串均可以在模板串里出现(如果有模板串的话),并不能保证其它的两两匹配
ull x,y;
sort(a+1,a+1+n,cmp1); // 开始匹配左边的,从小到大匹配,左右方便很多(不愧是大佬)
for( int i=1 ; i<n ; i++ ){
if( a[i].x==0 )
continue;
x=has[a[i].l+a[i].x-1];
y=has[a[i+1].l+a[i].x-1];
if( x!=y ){
pd=0;
break;
}
}
if( !pd ){
printf("N\n");
continue;
}
sort(a+1,a+1+n,cmp2); // 匹配右边的
for( int i=1 ; i<n ; i++ ){
if( a[i].y==0 )
continue;
if( a[i].len==a[i].y ) x=has[a[i].r]; // 要进行判断我们要的最右边的字符串,是真个字符串的部分或者就是整个字符串
else x=has[a[i].r]-has[a[i].r-a[i].y];
if( a[i+1].len==a[i].y ) y=has[a[i+1].r];
else y=has[a[i+1].r]-has[a[i+1].r-a[i].y];
int len=a[i+1].len-a[i].len; // 不要忘记hash中的求部分hash要乘差间
if( len>=0 )
x*=mi[len];
else
y*=mi[-len];
if( x!=y ){
pd=0;
break;
}
}
if( !pd )
printf("N\n");
else
printf("Y\n");
}
return 0;
}