bzoj 3574: [Hnoi2014]抄卡组 (字符串Hash)

来源:互联网 发布:内涵吧源码 编辑:程序博客网 时间:2024/06/18 10:46

题目描述

传送门

题目大意:’*’可以匹配任意长度个的字符(包含0个),问所有字符串是否两两匹配。

题解

对于两个都包含通配符的字符串,只要Lcp,Lcs的长度都等于到通配符较短的串的长度。
例如aaaa*aaaa和a*aa
只要LCP的长度等于1,LCS的长度等于2即可。剩下的中间部分可以通过通配符进行调整。
按照到第一个通配符的长度排序,然后相邻的用hash判断一下即可。
但是有一种情况 aaaaaaaa和aaa*5*aa,只通过上面的方式判断是不行的。
对于存在完整串的判断,我们需要对于不完整串依次判断他的每一部分是否在完整串中顺序出现。
这个的话直接扫描就可以了。
这道题的读入比较恶心,Re与WA的同学建议先考虑读入的问题。

代码

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#define N 10550003#define ULL unsigned long long #define p 2000001001using namespace std;struct data{    int l,r,x,y,len,cnt;}a[1000005];int n,m,T;ULL mi[N],hash[N];char s[N],ch[N];int cmp(data a,data b){    return a.x<b.x;}int cmp1(data a,data b){    return a.y<b.y;}string S;template<typename T>inline void read(T &res){    res=0;T fh=1;char ch=getchar();    while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();    if(ch=='-')fh=-1,ch=getchar();    while(ch>='0'&&ch<='9')res=res*10+ch-'0',ch=getchar();    res*=fh;}inline int gi(){int x;read(x);return x;}int main(){    freopen("input.txt","r",stdin);    //freopen("hs.out","w",stdout);    T=gi(); mi[0]=1;    for (int i=1;i<=10500000;i++) mi[i]=mi[i-1]*p;    while (T--) {        n=gi();        m=0; int mx=N,opt;         for (int i=1;i<=n;i++) {            getline(cin,S);            int len=S.size();            for (int j=0;j<len;j++) s[j+1]=S[j];            int l=len+1,r=0;            for (int j=1;j<=len;j++) {                ch[j+m]=s[j];                if (j==1) hash[j+m]=mi[j]*s[j];                else hash[j+m]=hash[j+m-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;            m+=len;            if (l!=len+1) a[i].x=l,a[i].y=len-r+1;            else {               a[i].x=a[i].y=len;               mx=min(mx,len);               if (mx==len) opt=i;            }        }        bool pd=true;        for (int i=1;i<=n;i++)         if (a[i].len-a[i].cnt>mx) {            pd=false;            break;         }        if (!pd) {            printf("N\n");            continue;        }        if(mx!=N) {            for (int i=1;i<=n;i++) {                if (i==opt) continue;                int t=a[i].l-1; int l=a[opt].l;                for (int j=1;j<=a[i].len;j++) {                    if (ch[t+j]=='*') continue;                    while (ch[t+j]!=ch[l]&&l<=a[opt].r) l++;                    if (ch[t+j]!=ch[l]) {                        pd=false;                        break;                    }                    l++;                }                if (!pd) break;            }        }        sort(a+1,a+n+1,cmp);         for (int i=1;i<n;i++) {            int t=a[i].x;            if (!t) continue;            if (hash[a[i].l+t-1]!=hash[a[i+1].l+t-1]) pd=false;            if (!pd) break;        }        if (!pd) {          printf("N\n");          continue;        }        sort(a+1,a+n+1,cmp1);        for (int i=1;i<n;i++) {           int t=a[i].y;            if (!t) continue;           ULL x,y;           if (t!=a[i].len) x=hash[a[i].r]-hash[a[i].r-t];           else x=hash[a[i].r];           if (t!=a[i+1].len) y=hash[a[i+1].r]-hash[a[i+1].r-t];           else y=hash[a[i+1].r];           int l=a[i+1].len-a[i].len;           if (l>=0) x*=mi[l];           else y*=mi[-l];           if (x!=y) pd=false;           if (!pd) break;        }        if (pd) printf("Y\n");        else printf("N\n");    }}
原创粉丝点击